@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.
@@ -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
+ }
@@ -53,11 +53,15 @@ class CodeBakersServer {
53
53
  server;
54
54
  apiKey;
55
55
  apiUrl;
56
+ trialState;
57
+ authMode;
56
58
  autoUpdateChecked = false;
57
59
  autoUpdateInProgress = false;
58
60
  constructor() {
59
61
  this.apiKey = (0, config_js_1.getApiKey)();
60
62
  this.apiUrl = (0, config_js_1.getApiUrl)();
63
+ this.trialState = (0, config_js_1.getTrialState)();
64
+ this.authMode = (0, config_js_1.getAuthMode)();
61
65
  this.server = new index_js_1.Server({
62
66
  name: 'codebakers',
63
67
  version: '1.0.0',
@@ -72,12 +76,25 @@ class CodeBakersServer {
72
76
  // Silently ignore errors - don't interrupt user
73
77
  });
74
78
  }
79
+ /**
80
+ * Get authorization headers for API requests
81
+ * Supports both API key (paid users) and trial ID (free users)
82
+ */
83
+ getAuthHeaders() {
84
+ if (this.apiKey) {
85
+ return { 'Authorization': `Bearer ${this.apiKey}` };
86
+ }
87
+ if (this.trialState?.trialId) {
88
+ return { 'X-Trial-Id': this.trialState.trialId };
89
+ }
90
+ return {};
91
+ }
75
92
  /**
76
93
  * Automatically check for and apply pattern updates
77
94
  * Runs silently in background - no user intervention needed
78
95
  */
79
96
  async checkAndAutoUpdate() {
80
- if (this.autoUpdateChecked || this.autoUpdateInProgress || !this.apiKey) {
97
+ if (this.autoUpdateChecked || this.autoUpdateInProgress || this.authMode === 'none') {
81
98
  return;
82
99
  }
83
100
  this.autoUpdateInProgress = true;
@@ -110,7 +127,7 @@ class CodeBakersServer {
110
127
  }
111
128
  // Fetch latest version
112
129
  const response = await fetch(`${this.apiUrl}/api/content/version`, {
113
- headers: { 'Authorization': `Bearer ${this.apiKey}` },
130
+ headers: this.getAuthHeaders(),
114
131
  });
115
132
  if (!response.ok) {
116
133
  this.autoUpdateInProgress = false;
@@ -128,7 +145,7 @@ class CodeBakersServer {
128
145
  }
129
146
  // Fetch full content and update
130
147
  const contentResponse = await fetch(`${this.apiUrl}/api/content`, {
131
- headers: { 'Authorization': `Bearer ${this.apiKey}` },
148
+ headers: this.getAuthHeaders(),
132
149
  });
133
150
  if (!contentResponse.ok) {
134
151
  this.autoUpdateInProgress = false;
@@ -362,7 +379,7 @@ class CodeBakersServer {
362
379
  let latest = null;
363
380
  try {
364
381
  const response = await fetch(`${this.apiUrl}/api/content/version`, {
365
- headers: this.apiKey ? { 'Authorization': `Bearer ${this.apiKey}` } : {},
382
+ headers: this.getAuthHeaders(),
366
383
  });
367
384
  if (response.ok) {
368
385
  latest = await response.json();
@@ -805,8 +822,26 @@ class CodeBakersServer {
805
822
  }));
806
823
  // Handle tool calls
807
824
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
808
- if (!this.apiKey) {
809
- throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not logged in. Run `codebakers login` first.');
825
+ // Check access: API key OR valid trial
826
+ if (this.authMode === 'none') {
827
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not logged in. Run `codebakers go` to start a free trial, or `codebakers setup` if you have an account.');
828
+ }
829
+ // Check if trial expired
830
+ if (this.authMode === 'trial' && (0, config_js_1.isTrialExpired)()) {
831
+ const trialState = (0, config_js_1.getTrialState)();
832
+ if (trialState?.stage === 'anonymous') {
833
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Trial expired. Run `codebakers extend` to add 7 more days with GitHub, or `codebakers billing` to upgrade.');
834
+ }
835
+ else {
836
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Trial expired. Run `codebakers billing` to upgrade to a paid plan.');
837
+ }
838
+ }
839
+ // Show warning if trial expiring soon
840
+ if (this.authMode === 'trial') {
841
+ const daysRemaining = (0, config_js_1.getTrialDaysRemaining)();
842
+ if (daysRemaining <= 2) {
843
+ console.error(`[CodeBakers] Trial expires in ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''}. Run 'codebakers extend' or 'codebakers billing'.`);
844
+ }
810
845
  }
811
846
  const { name, arguments: args } = request.params;
812
847
  switch (name) {
@@ -872,7 +907,7 @@ class CodeBakersServer {
872
907
  method: 'POST',
873
908
  headers: {
874
909
  'Content-Type': 'application/json',
875
- Authorization: `Bearer ${this.apiKey}`,
910
+ ...this.getAuthHeaders(),
876
911
  },
877
912
  body: JSON.stringify({
878
913
  prompt: userRequest,
@@ -981,9 +1016,7 @@ Show the user what their simple request was expanded into, then proceed with the
981
1016
  async handleListPatterns() {
982
1017
  const response = await fetch(`${this.apiUrl}/api/patterns`, {
983
1018
  method: 'GET',
984
- headers: {
985
- Authorization: `Bearer ${this.apiKey}`,
986
- },
1019
+ headers: this.getAuthHeaders(),
987
1020
  });
988
1021
  if (!response.ok) {
989
1022
  const error = await response.json().catch(() => ({}));
@@ -1026,7 +1059,7 @@ Show the user what their simple request was expanded into, then proceed with the
1026
1059
  method: 'POST',
1027
1060
  headers: {
1028
1061
  'Content-Type': 'application/json',
1029
- Authorization: `Bearer ${this.apiKey}`,
1062
+ ...this.getAuthHeaders(),
1030
1063
  },
1031
1064
  body: JSON.stringify({ patterns }),
1032
1065
  });
@@ -1043,7 +1076,7 @@ Show the user what their simple request was expanded into, then proceed with the
1043
1076
  method: 'POST',
1044
1077
  headers: {
1045
1078
  'Content-Type': 'application/json',
1046
- Authorization: `Bearer ${this.apiKey}`,
1079
+ ...this.getAuthHeaders(),
1047
1080
  },
1048
1081
  body: JSON.stringify({ query }),
1049
1082
  });
@@ -1291,7 +1324,7 @@ Show the user what their simple request was expanded into, then proceed with the
1291
1324
  results.push('\n## Installing CodeBakers Patterns...\n');
1292
1325
  const response = await fetch(`${this.apiUrl}/api/content`, {
1293
1326
  method: 'GET',
1294
- headers: { Authorization: `Bearer ${this.apiKey}` },
1327
+ headers: this.getAuthHeaders(),
1295
1328
  });
1296
1329
  if (response.ok) {
1297
1330
  const content = await response.json();
@@ -1671,7 +1704,7 @@ Or if user declines, call without fullDeploy:
1671
1704
  try {
1672
1705
  const response = await fetch(`${this.apiUrl}/api/content`, {
1673
1706
  method: 'GET',
1674
- headers: { Authorization: `Bearer ${this.apiKey}` },
1707
+ headers: this.getAuthHeaders(),
1675
1708
  });
1676
1709
  if (!response.ok) {
1677
1710
  throw new Error('Failed to fetch patterns from API');
@@ -2678,7 +2711,7 @@ Just describe what you want to build! I'll automatically:
2678
2711
  method: 'POST',
2679
2712
  headers: {
2680
2713
  'Content-Type': 'application/json',
2681
- Authorization: `Bearer ${this.apiKey}`,
2714
+ ...this.getAuthHeaders(),
2682
2715
  },
2683
2716
  body: JSON.stringify({
2684
2717
  category,
@@ -2725,7 +2758,7 @@ Just describe what you want to build! I'll automatically:
2725
2758
  method: 'POST',
2726
2759
  headers: {
2727
2760
  'Content-Type': 'application/json',
2728
- Authorization: `Bearer ${this.apiKey}`,
2761
+ ...this.getAuthHeaders(),
2729
2762
  },
2730
2763
  body: JSON.stringify({
2731
2764
  eventType,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codebakers/cli",
3
- "version": "2.9.0",
3
+ "version": "3.0.0",
4
4
  "description": "CodeBakers CLI - Production patterns for AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -0,0 +1,99 @@
1
+ import chalk from 'chalk';
2
+ import { execSync } from 'child_process';
3
+ import { getApiUrl, getApiKey, getTrialState, isTrialExpired, getTrialDaysRemaining } from '../config.js';
4
+
5
+ /**
6
+ * Open a URL in the default browser
7
+ */
8
+ function openBrowser(url: string): void {
9
+ const platform = process.platform;
10
+
11
+ try {
12
+ if (platform === 'win32') {
13
+ execSync(`start "" "${url}"`, { stdio: 'ignore', shell: 'cmd.exe' });
14
+ } else if (platform === 'darwin') {
15
+ execSync(`open "${url}"`, { stdio: 'ignore' });
16
+ } else {
17
+ execSync(`xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`, {
18
+ stdio: 'ignore',
19
+ shell: '/bin/sh',
20
+ });
21
+ }
22
+ } catch {
23
+ console.log(chalk.yellow(`\n Could not open browser automatically.`));
24
+ console.log(chalk.gray(` Please open this URL manually:\n`));
25
+ console.log(chalk.cyan(` ${url}\n`));
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Open billing page for subscription management
31
+ */
32
+ export async function billing(): Promise<void> {
33
+ console.log(chalk.blue(`
34
+ ╔═══════════════════════════════════════════════════════════╗
35
+ ║ ║
36
+ ║ ${chalk.bold.white('CodeBakers Billing & Subscription')} ║
37
+ ║ ║
38
+ ╚═══════════════════════════════════════════════════════════╝
39
+ `));
40
+
41
+ // Check current status
42
+ const apiKey = getApiKey();
43
+ const trial = getTrialState();
44
+
45
+ if (apiKey) {
46
+ console.log(chalk.green(' ✓ You have an active subscription\n'));
47
+ console.log(chalk.gray(' Opening settings page to manage your subscription...\n'));
48
+
49
+ const apiUrl = getApiUrl();
50
+ const settingsUrl = `${apiUrl}/settings`;
51
+
52
+ openBrowser(settingsUrl);
53
+ console.log(chalk.gray(` ${settingsUrl}\n`));
54
+ return;
55
+ }
56
+
57
+ if (trial) {
58
+ if (isTrialExpired()) {
59
+ console.log(chalk.yellow(' ⚠️ Your trial has expired\n'));
60
+ } else {
61
+ const daysRemaining = getTrialDaysRemaining();
62
+ console.log(chalk.gray(` Trial: ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining\n`));
63
+ }
64
+ }
65
+
66
+ // Show pricing
67
+ console.log(chalk.white(' Choose your plan:\n'));
68
+
69
+ console.log(chalk.cyan(' Pro ') + chalk.white('- $49/month ') + chalk.gray('(1 seat)'));
70
+ console.log(chalk.gray(' All 40 pattern modules'));
71
+ console.log(chalk.gray(' Unlimited projects'));
72
+ console.log(chalk.gray(' Priority support\n'));
73
+
74
+ console.log(chalk.cyan(' Team ') + chalk.white('- $149/month ') + chalk.gray('(5 seats)'));
75
+ console.log(chalk.gray(' Everything in Pro'));
76
+ console.log(chalk.gray(' Team collaboration'));
77
+ console.log(chalk.gray(' Shared API keys\n'));
78
+
79
+ console.log(chalk.cyan(' Agency ') + chalk.white('- $349/month ') + chalk.gray('(unlimited seats)'));
80
+ console.log(chalk.gray(' Everything in Team'));
81
+ console.log(chalk.gray(' White-label option'));
82
+ console.log(chalk.gray(' Dedicated support\n'));
83
+
84
+ console.log(chalk.gray(' Enterprise pricing available for large teams.\n'));
85
+
86
+ // Open billing page
87
+ const apiUrl = getApiUrl();
88
+ const billingUrl = `${apiUrl}/billing`;
89
+
90
+ console.log(chalk.white(' Opening billing page...\n'));
91
+
92
+ openBrowser(billingUrl);
93
+
94
+ console.log(chalk.gray(` ${billingUrl}\n`));
95
+
96
+ console.log(chalk.gray(' After subscribing, run:'));
97
+ console.log(chalk.cyan(' codebakers setup\n'));
98
+ console.log(chalk.gray(' to configure your API key.\n'));
99
+ }
@@ -0,0 +1,157 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { execSync } from 'child_process';
4
+ import {
5
+ getTrialState,
6
+ setTrialState,
7
+ getApiUrl,
8
+ getApiKey,
9
+ isTrialExpired,
10
+ type TrialState,
11
+ } from '../config.js';
12
+
13
+ /**
14
+ * Open a URL in the default browser
15
+ */
16
+ function openBrowser(url: string): void {
17
+ const platform = process.platform;
18
+
19
+ try {
20
+ if (platform === 'win32') {
21
+ execSync(`start "" "${url}"`, { stdio: 'ignore', shell: 'cmd.exe' });
22
+ } else if (platform === 'darwin') {
23
+ execSync(`open "${url}"`, { stdio: 'ignore' });
24
+ } else {
25
+ // Linux - try common browsers
26
+ execSync(`xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}"`, {
27
+ stdio: 'ignore',
28
+ shell: '/bin/sh',
29
+ });
30
+ }
31
+ } catch {
32
+ console.log(chalk.yellow(`\n Could not open browser automatically.`));
33
+ console.log(chalk.gray(` Please open this URL manually:\n`));
34
+ console.log(chalk.cyan(` ${url}\n`));
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Sleep for a specified number of milliseconds
40
+ */
41
+ function sleep(ms: number): Promise<void> {
42
+ return new Promise((resolve) => setTimeout(resolve, ms));
43
+ }
44
+
45
+ /**
46
+ * Extend trial by connecting GitHub account
47
+ */
48
+ export async function extend(): Promise<void> {
49
+ console.log(chalk.blue(`
50
+ ╔═══════════════════════════════════════════════════════════╗
51
+ ║ ║
52
+ ║ ${chalk.bold.white('Extend Your Trial with GitHub')} ║
53
+ ║ ║
54
+ ╚═══════════════════════════════════════════════════════════╝
55
+ `));
56
+
57
+ // Check if user already has an API key (paid user)
58
+ const apiKey = getApiKey();
59
+ if (apiKey) {
60
+ console.log(chalk.green(' ✓ You\'re already logged in with an API key!\n'));
61
+ console.log(chalk.gray(' You have unlimited access. No extension needed.\n'));
62
+ return;
63
+ }
64
+
65
+ // Check for existing trial
66
+ const trial = getTrialState();
67
+
68
+ if (!trial) {
69
+ console.log(chalk.yellow(' No trial found.\n'));
70
+ console.log(chalk.white(' Start your free trial first:\n'));
71
+ console.log(chalk.cyan(' codebakers go\n'));
72
+ return;
73
+ }
74
+
75
+ // Check if already extended
76
+ if (trial.stage === 'extended') {
77
+ console.log(chalk.yellow(' Your trial has already been extended.\n'));
78
+
79
+ if (isTrialExpired()) {
80
+ console.log(chalk.white(' Ready to upgrade? $49/month for unlimited access:\n'));
81
+ console.log(chalk.cyan(' codebakers upgrade\n'));
82
+ } else {
83
+ const expiresAt = new Date(trial.expiresAt);
84
+ const daysRemaining = Math.max(0, Math.ceil((expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24)));
85
+ console.log(chalk.gray(` ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining.\n`));
86
+ }
87
+ return;
88
+ }
89
+
90
+ // Check if converted
91
+ if (trial.stage === 'converted') {
92
+ console.log(chalk.green(' ✓ You\'ve upgraded to a paid plan!\n'));
93
+ console.log(chalk.gray(' Run ') + chalk.cyan('codebakers setup') + chalk.gray(' to configure your API key.\n'));
94
+ return;
95
+ }
96
+
97
+ // Open browser for GitHub OAuth
98
+ const apiUrl = getApiUrl();
99
+ const authUrl = `${apiUrl}/api/auth/github?trial_id=${trial.trialId}`;
100
+
101
+ console.log(chalk.white(' Opening browser for GitHub authorization...\n'));
102
+
103
+ openBrowser(authUrl);
104
+
105
+ console.log(chalk.gray(' Waiting for authorization...'));
106
+ console.log(chalk.gray(' (This may take a moment)\n'));
107
+
108
+ // Poll for completion
109
+ const spinner = ora('Checking authorization status...').start();
110
+ let extended = false;
111
+ let pollCount = 0;
112
+ const maxPolls = 60; // 2 minutes max
113
+
114
+ while (pollCount < maxPolls && !extended) {
115
+ await sleep(2000);
116
+ pollCount++;
117
+
118
+ try {
119
+ const response = await fetch(`${apiUrl}/api/trial/status?trialId=${trial.trialId}`);
120
+ const data = await response.json();
121
+
122
+ if (data.stage === 'extended') {
123
+ extended = true;
124
+
125
+ // Update local trial state
126
+ const updatedTrial: TrialState = {
127
+ ...trial,
128
+ stage: 'extended',
129
+ expiresAt: data.expiresAt,
130
+ extendedAt: new Date().toISOString(),
131
+ ...(data.githubUsername && { githubUsername: data.githubUsername }),
132
+ };
133
+
134
+ setTrialState(updatedTrial);
135
+ spinner.succeed('Trial extended!');
136
+ }
137
+ } catch {
138
+ // Ignore polling errors
139
+ }
140
+ }
141
+
142
+ if (extended) {
143
+ console.log(chalk.green(`
144
+ ╔═══════════════════════════════════════════════════════════╗
145
+ ║ ✅ Trial Extended! ║
146
+ ║ ║
147
+ ║ ${chalk.white('You have 7 more days to build with CodeBakers.')} ║
148
+ ║ ║
149
+ ║ ${chalk.gray('Keep building - your project is waiting!')} ║
150
+ ╚═══════════════════════════════════════════════════════════╝
151
+ `));
152
+ } else {
153
+ spinner.warn('Authorization timed out');
154
+ console.log(chalk.yellow('\n Please try again or authorize manually:\n'));
155
+ console.log(chalk.cyan(` ${authUrl}\n`));
156
+ }
157
+ }