@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/src/mcp/server.ts CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  ErrorCode,
9
9
  McpError,
10
10
  } from '@modelcontextprotocol/sdk/types.js';
11
- import { getApiKey, getApiUrl, getExperienceLevel, setExperienceLevel, getServiceKey, setServiceKey, type ExperienceLevel } from '../config.js';
11
+ import { getApiKey, getApiUrl, getExperienceLevel, setExperienceLevel, getServiceKey, setServiceKey, getTrialState, isTrialExpired, getTrialDaysRemaining, hasValidAccess, getAuthMode, type ExperienceLevel, type TrialState } from '../config.js';
12
12
  import { audit as runAudit } from '../commands/audit.js';
13
13
  import { heal as runHeal } from '../commands/heal.js';
14
14
  import { getCliVersion } from '../lib/api.js';
@@ -61,12 +61,16 @@ class CodeBakersServer {
61
61
  private server: Server;
62
62
  private apiKey: string | null;
63
63
  private apiUrl: string;
64
+ private trialState: TrialState | null;
65
+ private authMode: 'apiKey' | 'trial' | 'none';
64
66
  private autoUpdateChecked = false;
65
67
  private autoUpdateInProgress = false;
66
68
 
67
69
  constructor() {
68
70
  this.apiKey = getApiKey();
69
71
  this.apiUrl = getApiUrl();
72
+ this.trialState = getTrialState();
73
+ this.authMode = getAuthMode();
70
74
 
71
75
  this.server = new Server(
72
76
  {
@@ -88,12 +92,28 @@ class CodeBakersServer {
88
92
  });
89
93
  }
90
94
 
95
+ /**
96
+ * Get authorization headers for API requests
97
+ * Supports both API key (paid users) and trial ID (free users)
98
+ */
99
+ private getAuthHeaders(): Record<string, string> {
100
+ if (this.apiKey) {
101
+ return { 'Authorization': `Bearer ${this.apiKey}` };
102
+ }
103
+
104
+ if (this.trialState?.trialId) {
105
+ return { 'X-Trial-Id': this.trialState.trialId };
106
+ }
107
+
108
+ return {};
109
+ }
110
+
91
111
  /**
92
112
  * Automatically check for and apply pattern updates
93
113
  * Runs silently in background - no user intervention needed
94
114
  */
95
115
  private async checkAndAutoUpdate(): Promise<void> {
96
- if (this.autoUpdateChecked || this.autoUpdateInProgress || !this.apiKey) {
116
+ if (this.autoUpdateChecked || this.autoUpdateInProgress || this.authMode === 'none') {
97
117
  return;
98
118
  }
99
119
 
@@ -131,7 +151,7 @@ class CodeBakersServer {
131
151
 
132
152
  // Fetch latest version
133
153
  const response = await fetch(`${this.apiUrl}/api/content/version`, {
134
- headers: { 'Authorization': `Bearer ${this.apiKey}` },
154
+ headers: this.getAuthHeaders(),
135
155
  });
136
156
 
137
157
  if (!response.ok) {
@@ -153,7 +173,7 @@ class CodeBakersServer {
153
173
 
154
174
  // Fetch full content and update
155
175
  const contentResponse = await fetch(`${this.apiUrl}/api/content`, {
156
- headers: { 'Authorization': `Bearer ${this.apiKey}` },
176
+ headers: this.getAuthHeaders(),
157
177
  });
158
178
 
159
179
  if (!contentResponse.ok) {
@@ -400,7 +420,7 @@ class CodeBakersServer {
400
420
  let latest: { version: string; moduleCount: number } | null = null;
401
421
  try {
402
422
  const response = await fetch(`${this.apiUrl}/api/content/version`, {
403
- headers: this.apiKey ? { 'Authorization': `Bearer ${this.apiKey}` } : {},
423
+ headers: this.getAuthHeaders(),
404
424
  });
405
425
  if (response.ok) {
406
426
  latest = await response.json();
@@ -879,13 +899,38 @@ class CodeBakersServer {
879
899
 
880
900
  // Handle tool calls
881
901
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
882
- if (!this.apiKey) {
902
+ // Check access: API key OR valid trial
903
+ if (this.authMode === 'none') {
883
904
  throw new McpError(
884
905
  ErrorCode.InvalidRequest,
885
- 'Not logged in. Run `codebakers login` first.'
906
+ 'Not logged in. Run `codebakers go` to start a free trial, or `codebakers setup` if you have an account.'
886
907
  );
887
908
  }
888
909
 
910
+ // Check if trial expired
911
+ if (this.authMode === 'trial' && isTrialExpired()) {
912
+ const trialState = getTrialState();
913
+ if (trialState?.stage === 'anonymous') {
914
+ throw new McpError(
915
+ ErrorCode.InvalidRequest,
916
+ 'Trial expired. Run `codebakers extend` to add 7 more days with GitHub, or `codebakers billing` to upgrade.'
917
+ );
918
+ } else {
919
+ throw new McpError(
920
+ ErrorCode.InvalidRequest,
921
+ 'Trial expired. Run `codebakers billing` to upgrade to a paid plan.'
922
+ );
923
+ }
924
+ }
925
+
926
+ // Show warning if trial expiring soon
927
+ if (this.authMode === 'trial') {
928
+ const daysRemaining = getTrialDaysRemaining();
929
+ if (daysRemaining <= 2) {
930
+ console.error(`[CodeBakers] Trial expires in ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''}. Run 'codebakers extend' or 'codebakers billing'.`);
931
+ }
932
+ }
933
+
889
934
  const { name, arguments: args } = request.params;
890
935
 
891
936
  switch (name) {
@@ -977,7 +1022,7 @@ class CodeBakersServer {
977
1022
  method: 'POST',
978
1023
  headers: {
979
1024
  'Content-Type': 'application/json',
980
- Authorization: `Bearer ${this.apiKey}`,
1025
+ ...this.getAuthHeaders(),
981
1026
  },
982
1027
  body: JSON.stringify({
983
1028
  prompt: userRequest,
@@ -1103,9 +1148,7 @@ Show the user what their simple request was expanded into, then proceed with the
1103
1148
  private async handleListPatterns() {
1104
1149
  const response = await fetch(`${this.apiUrl}/api/patterns`, {
1105
1150
  method: 'GET',
1106
- headers: {
1107
- Authorization: `Bearer ${this.apiKey}`,
1108
- },
1151
+ headers: this.getAuthHeaders(),
1109
1152
  });
1110
1153
 
1111
1154
  if (!response.ok) {
@@ -1164,7 +1207,7 @@ Show the user what their simple request was expanded into, then proceed with the
1164
1207
  method: 'POST',
1165
1208
  headers: {
1166
1209
  'Content-Type': 'application/json',
1167
- Authorization: `Bearer ${this.apiKey}`,
1210
+ ...this.getAuthHeaders(),
1168
1211
  },
1169
1212
  body: JSON.stringify({ patterns }),
1170
1213
  });
@@ -1188,7 +1231,7 @@ Show the user what their simple request was expanded into, then proceed with the
1188
1231
  method: 'POST',
1189
1232
  headers: {
1190
1233
  'Content-Type': 'application/json',
1191
- Authorization: `Bearer ${this.apiKey}`,
1234
+ ...this.getAuthHeaders(),
1192
1235
  },
1193
1236
  body: JSON.stringify({ query }),
1194
1237
  });
@@ -1475,7 +1518,7 @@ Show the user what their simple request was expanded into, then proceed with the
1475
1518
 
1476
1519
  const response = await fetch(`${this.apiUrl}/api/content`, {
1477
1520
  method: 'GET',
1478
- headers: { Authorization: `Bearer ${this.apiKey}` },
1521
+ headers: this.getAuthHeaders(),
1479
1522
  });
1480
1523
 
1481
1524
  if (response.ok) {
@@ -1881,7 +1924,7 @@ Or if user declines, call without fullDeploy:
1881
1924
  try {
1882
1925
  const response = await fetch(`${this.apiUrl}/api/content`, {
1883
1926
  method: 'GET',
1884
- headers: { Authorization: `Bearer ${this.apiKey}` },
1927
+ headers: this.getAuthHeaders(),
1885
1928
  });
1886
1929
 
1887
1930
  if (!response.ok) {
@@ -2991,7 +3034,7 @@ Just describe what you want to build! I'll automatically:
2991
3034
  method: 'POST',
2992
3035
  headers: {
2993
3036
  'Content-Type': 'application/json',
2994
- Authorization: `Bearer ${this.apiKey}`,
3037
+ ...this.getAuthHeaders(),
2995
3038
  },
2996
3039
  body: JSON.stringify({
2997
3040
  category,
@@ -3043,7 +3086,7 @@ Just describe what you want to build! I'll automatically:
3043
3086
  method: 'POST',
3044
3087
  headers: {
3045
3088
  'Content-Type': 'application/json',
3046
- Authorization: `Bearer ${this.apiKey}`,
3089
+ ...this.getAuthHeaders(),
3047
3090
  },
3048
3091
  body: JSON.stringify({
3049
3092
  eventType,