codecane 1.0.207 → 1.0.237

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.
Files changed (213) hide show
  1. package/dist/background-process-manager.d.ts +50 -0
  2. package/dist/background-process-manager.js +320 -0
  3. package/dist/background-process-manager.js.map +1 -0
  4. package/dist/browser-runner.js +40 -34
  5. package/dist/browser-runner.js.map +1 -1
  6. package/dist/checkpoints/checkpoint-manager.d.ts +30 -9
  7. package/dist/checkpoints/checkpoint-manager.js +119 -32
  8. package/dist/checkpoints/checkpoint-manager.js.map +1 -1
  9. package/dist/checkpoints/file-manager.js +14 -6
  10. package/dist/checkpoints/file-manager.js.map +1 -1
  11. package/dist/cli-handlers/api-key.d.ts +25 -0
  12. package/dist/cli-handlers/api-key.js +66 -0
  13. package/dist/cli-handlers/api-key.js.map +1 -0
  14. package/dist/cli-handlers/checkpoint.d.ts +18 -0
  15. package/dist/cli-handlers/checkpoint.js +195 -0
  16. package/dist/cli-handlers/checkpoint.js.map +1 -0
  17. package/dist/cli-handlers/diff.d.ts +2 -0
  18. package/dist/cli-handlers/diff.js +31 -0
  19. package/dist/cli-handlers/diff.js.map +1 -0
  20. package/dist/cli-handlers/easter-egg.d.ts +1 -0
  21. package/dist/cli-handlers/easter-egg.js +126 -0
  22. package/dist/cli-handlers/easter-egg.js.map +1 -0
  23. package/dist/cli-handlers/inititalization-flow.d.ts +1 -0
  24. package/dist/cli-handlers/inititalization-flow.js +24 -0
  25. package/dist/cli-handlers/inititalization-flow.js.map +1 -0
  26. package/dist/cli.d.ts +8 -11
  27. package/dist/cli.js +171 -320
  28. package/dist/cli.js.map +1 -1
  29. package/dist/client.d.ts +34 -124
  30. package/dist/client.js +395 -154
  31. package/dist/client.js.map +1 -1
  32. package/dist/code-map/languages.d.ts +0 -1
  33. package/dist/code-map/languages.js +23 -224
  34. package/dist/code-map/languages.js.map +1 -1
  35. package/dist/code-map/parse.js +1 -0
  36. package/dist/code-map/parse.js.map +1 -1
  37. package/dist/code-map/tsconfig.tsbuildinfo +1 -1
  38. package/dist/common/actions.d.ts +369 -826
  39. package/dist/common/actions.js +25 -31
  40. package/dist/common/actions.js.map +1 -1
  41. package/dist/common/api-keys/constants.d.ts +8 -0
  42. package/dist/common/api-keys/constants.js +26 -0
  43. package/dist/common/api-keys/constants.js.map +1 -0
  44. package/dist/common/api-keys/crypto.d.ts +24 -0
  45. package/dist/common/api-keys/crypto.js +186 -0
  46. package/dist/common/api-keys/crypto.js.map +1 -0
  47. package/dist/common/bigquery/client.d.ts +15 -0
  48. package/dist/common/bigquery/client.js +246 -0
  49. package/dist/common/bigquery/client.js.map +1 -0
  50. package/dist/common/bigquery/schema.d.ts +55 -0
  51. package/dist/common/bigquery/schema.js +24 -0
  52. package/dist/common/bigquery/schema.js.map +1 -0
  53. package/dist/common/billing/auto-topup.d.ts +8 -0
  54. package/dist/common/billing/auto-topup.js +192 -0
  55. package/dist/common/billing/auto-topup.js.map +1 -0
  56. package/dist/common/billing/balance-calculator.d.ts +57 -0
  57. package/dist/common/billing/balance-calculator.js +218 -0
  58. package/dist/common/billing/balance-calculator.js.map +1 -0
  59. package/dist/common/billing/conversion.d.ts +9 -0
  60. package/dist/common/billing/conversion.js +20 -0
  61. package/dist/common/billing/conversion.js.map +1 -0
  62. package/dist/common/billing/credit-conversion.d.ts +24 -0
  63. package/dist/common/billing/credit-conversion.js +48 -0
  64. package/dist/common/billing/credit-conversion.js.map +1 -0
  65. package/dist/common/billing/grant-credits.d.ts +43 -0
  66. package/dist/common/billing/grant-credits.js +266 -0
  67. package/dist/common/billing/grant-credits.js.map +1 -0
  68. package/dist/common/billing/plans.d.ts +13 -0
  69. package/dist/common/billing/plans.js +44 -0
  70. package/dist/common/billing/plans.js.map +1 -0
  71. package/dist/common/browser-actions.d.ts +1 -1
  72. package/dist/common/browser-actions.js +4 -4
  73. package/dist/common/browser-actions.js.map +1 -1
  74. package/dist/common/constants/grant-priorities.d.ts +2 -0
  75. package/dist/common/constants/grant-priorities.js +10 -0
  76. package/dist/common/constants/grant-priorities.js.map +1 -0
  77. package/dist/common/constants/tools.d.ts +2 -0
  78. package/dist/common/constants/tools.js +24 -3
  79. package/dist/common/constants/tools.js.map +1 -1
  80. package/dist/common/constants.d.ts +93 -5
  81. package/dist/common/constants.js +111 -10
  82. package/dist/common/constants.js.map +1 -1
  83. package/dist/common/db/env.mjs +5 -5
  84. package/dist/common/db/env.mjs.map +1 -1
  85. package/dist/common/db/schema.d.ts +413 -83
  86. package/dist/common/db/schema.js +70 -9
  87. package/dist/common/db/schema.js.map +1 -1
  88. package/dist/common/db/transaction.d.ts +12 -0
  89. package/dist/common/db/transaction.js +36 -0
  90. package/dist/common/db/transaction.js.map +1 -0
  91. package/dist/common/env.mjs +11 -8
  92. package/dist/common/env.mjs.map +1 -1
  93. package/dist/common/json-config/__tests__/__snapshots__/stringify-schema.test.js.snap +66 -0
  94. package/dist/common/json-config/__tests__/stringify-schema.test.d.ts +1 -0
  95. package/dist/common/json-config/__tests__/stringify-schema.test.js +66 -0
  96. package/dist/common/json-config/__tests__/stringify-schema.test.js.map +1 -0
  97. package/dist/common/json-config/constants.d.ts +82 -0
  98. package/dist/common/json-config/constants.js +48 -0
  99. package/dist/common/json-config/constants.js.map +1 -0
  100. package/dist/common/json-config/parser.d.ts +7 -0
  101. package/dist/common/json-config/parser.js +51 -0
  102. package/dist/common/json-config/parser.js.map +1 -0
  103. package/dist/common/json-config/stringify-schema.d.ts +10 -0
  104. package/dist/common/json-config/stringify-schema.js +102 -0
  105. package/dist/common/json-config/stringify-schema.js.map +1 -0
  106. package/dist/common/project-file-tree.d.ts +2 -1
  107. package/dist/common/project-file-tree.js +59 -27
  108. package/dist/common/project-file-tree.js.map +1 -1
  109. package/dist/common/types/agent-state.d.ts +25 -22
  110. package/dist/common/types/agent-state.js +2 -0
  111. package/dist/common/types/agent-state.js.map +1 -1
  112. package/dist/common/types/grant.d.ts +2 -0
  113. package/dist/common/types/grant.js +10 -0
  114. package/dist/common/types/grant.js.map +1 -0
  115. package/dist/common/types/usage.d.ts +37 -18
  116. package/dist/common/types/usage.js +9 -6
  117. package/dist/common/types/usage.js.map +1 -1
  118. package/dist/common/util/__tests__/saxy.test.d.ts +1 -0
  119. package/dist/common/util/__tests__/saxy.test.js +262 -0
  120. package/dist/common/util/__tests__/saxy.test.js.map +1 -0
  121. package/dist/common/util/__tests__/string.test.js +2 -3
  122. package/dist/common/util/__tests__/string.test.js.map +1 -1
  123. package/dist/common/util/changes.d.ts +1 -0
  124. package/dist/common/util/changes.js +8 -2
  125. package/dist/common/util/changes.js.map +1 -1
  126. package/dist/common/util/dates.d.ts +10 -1
  127. package/dist/common/util/dates.js +11 -2
  128. package/dist/common/util/dates.js.map +1 -1
  129. package/dist/common/util/file.d.ts +10 -9
  130. package/dist/common/util/file.js +12 -4
  131. package/dist/common/util/file.js.map +1 -1
  132. package/dist/common/util/logger.d.ts +8 -0
  133. package/dist/common/util/logger.js +52 -0
  134. package/dist/common/util/logger.js.map +1 -0
  135. package/dist/common/util/lru-cache.d.ts +23 -2
  136. package/dist/common/util/lru-cache.js +44 -18
  137. package/dist/common/util/lru-cache.js.map +1 -1
  138. package/dist/common/util/messages.d.ts +3 -0
  139. package/dist/common/util/messages.js +50 -3
  140. package/dist/common/util/messages.js.map +1 -1
  141. package/dist/common/util/min-heap.js +2 -2
  142. package/dist/common/util/min-heap.js.map +1 -1
  143. package/dist/common/util/promise.d.ts +1 -1
  144. package/dist/common/util/promise.js +2 -2
  145. package/dist/common/util/promise.js.map +1 -1
  146. package/dist/common/util/saxy.d.ts +8 -0
  147. package/dist/common/util/saxy.js +35 -12
  148. package/dist/common/util/saxy.js.map +1 -1
  149. package/dist/common/util/string.d.ts +7 -1
  150. package/dist/common/util/string.js +20 -4
  151. package/dist/common/util/string.js.map +1 -1
  152. package/dist/common/util/stripe.d.ts +1 -0
  153. package/dist/common/util/stripe.js +10 -7
  154. package/dist/common/util/stripe.js.map +1 -1
  155. package/dist/common/util/sync-failure.d.ts +1 -0
  156. package/dist/common/util/sync-failure.js +57 -0
  157. package/dist/common/util/sync-failure.js.map +1 -0
  158. package/dist/common/websockets/websocket-client.d.ts +2 -1
  159. package/dist/common/websockets/websocket-client.js +34 -6
  160. package/dist/common/websockets/websocket-client.js.map +1 -1
  161. package/dist/common/websockets/websocket-schema.d.ts +1440 -2642
  162. package/dist/config.d.ts +2 -1
  163. package/dist/config.js +6 -3
  164. package/dist/config.js.map +1 -1
  165. package/dist/credentials.js +3 -2
  166. package/dist/credentials.js.map +1 -1
  167. package/dist/dev-process-manager.d.ts +10 -0
  168. package/dist/dev-process-manager.js +54 -0
  169. package/dist/dev-process-manager.js.map +1 -0
  170. package/dist/fingerprint.js +7 -2
  171. package/dist/fingerprint.js.map +1 -1
  172. package/dist/index.js +79 -74
  173. package/dist/index.js.map +1 -1
  174. package/dist/menu.js +20 -13
  175. package/dist/menu.js.map +1 -1
  176. package/dist/project-files.d.ts +10 -6
  177. package/dist/project-files.js +55 -32
  178. package/dist/project-files.js.map +1 -1
  179. package/dist/startup-process-handler.d.ts +2 -0
  180. package/dist/startup-process-handler.js +21 -0
  181. package/dist/startup-process-handler.js.map +1 -0
  182. package/dist/tool-handlers.d.ts +1 -0
  183. package/dist/tool-handlers.js +46 -23
  184. package/dist/tool-handlers.js.map +1 -1
  185. package/dist/types.d.ts +2 -0
  186. package/dist/update-codebuff.js +3 -1
  187. package/dist/update-codebuff.js.map +1 -1
  188. package/dist/utils/__tests__/background-process-manager.test.d.ts +1 -0
  189. package/dist/utils/__tests__/background-process-manager.test.js +289 -0
  190. package/dist/utils/__tests__/background-process-manager.test.js.map +1 -0
  191. package/dist/utils/__tests__/response-example-4-files.txt +621 -0
  192. package/dist/utils/__tests__/tool-renderers.test.d.ts +1 -0
  193. package/dist/utils/__tests__/tool-renderers.test.js +51 -0
  194. package/dist/utils/__tests__/tool-renderers.test.js.map +1 -0
  195. package/dist/utils/__tests__/xml-stream-parser.test.js +29 -21
  196. package/dist/utils/__tests__/xml-stream-parser.test.js.map +1 -1
  197. package/dist/utils/terminal.d.ts +12 -1
  198. package/dist/utils/terminal.js +148 -23
  199. package/dist/utils/terminal.js.map +1 -1
  200. package/dist/utils/tool-renderers.js +45 -17
  201. package/dist/utils/tool-renderers.js.map +1 -1
  202. package/dist/workers/checkpoint-worker.js +4 -3
  203. package/dist/workers/checkpoint-worker.js.map +1 -1
  204. package/package.json +20 -35
  205. package/dist/common/billing/quota-manager.d.ts +0 -29
  206. package/dist/common/billing/quota-manager.js +0 -213
  207. package/dist/common/billing/quota-manager.js.map +0 -1
  208. package/dist/common/util/tools.d.ts +0 -2
  209. package/dist/common/util/tools.js +0 -13
  210. package/dist/common/util/tools.js.map +0 -1
  211. package/dist/utils/logger.d.ts +0 -2
  212. package/dist/utils/logger.js +0 -33
  213. package/dist/utils/logger.js.map +0 -1
package/dist/client.js CHANGED
@@ -1,43 +1,25 @@
1
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 (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.Client = void 0;
30
7
  const child_process_1 = require("child_process");
31
- const fs = __importStar(require("fs"));
8
+ const fs_1 = require("fs");
32
9
  const path_1 = __importDefault(require("path"));
33
- const picocolors_1 = require("picocolors");
34
- const ts_pattern_1 = require("ts-pattern");
35
10
  const actions_1 = require("./common/actions");
36
- const constants_1 = require("./common/constants");
11
+ const constants_1 = require("./common/api-keys/constants");
12
+ const constants_2 = require("./common/constants");
13
+ const constants_3 = require("./common/json-config/constants");
37
14
  const agent_state_1 = require("./common/types/agent-state");
15
+ const string_1 = require("./common/util/string");
38
16
  const websocket_client_1 = require("./common/websockets/websocket-client");
39
- const chat_storage_1 = require("./chat-storage");
17
+ const picocolors_1 = require("picocolors");
18
+ const ts_pattern_1 = require("ts-pattern");
19
+ const zod_1 = require("zod");
20
+ const background_process_manager_1 = require("./background-process-manager");
40
21
  const browser_runner_1 = require("./browser-runner");
22
+ const chat_storage_1 = require("./chat-storage");
41
23
  const checkpoint_manager_1 = require("./checkpoints/checkpoint-manager");
42
24
  const config_1 = require("./config");
43
25
  const credentials_1 = require("./credentials");
@@ -46,37 +28,81 @@ const menu_1 = require("./menu");
46
28
  const project_files_1 = require("./project-files");
47
29
  const tool_handlers_1 = require("./tool-handlers");
48
30
  const spinner_1 = require("./utils/spinner");
49
- const xml_stream_parser_1 = require("./utils/xml-stream-parser");
50
31
  const tool_renderers_1 = require("./utils/tool-renderers");
32
+ const xml_stream_parser_1 = require("./utils/xml-stream-parser");
33
+ const web_scraper_1 = require("./web-scraper");
34
+ const web_scraper_2 = require("./web-scraper");
35
+ const array_1 = require("./common/util/array");
36
+ const LOW_BALANCE_THRESHOLD = 100;
37
+ const WARNING_CONFIG = {
38
+ [constants_2.UserState.LOGGED_OUT]: {
39
+ message: () => `Type "login" to unlock full access and get free credits!`,
40
+ threshold: 100,
41
+ },
42
+ [constants_2.UserState.DEPLETED]: {
43
+ message: () => [
44
+ (0, picocolors_1.red)(`\n❌ You have used all your credits.`),
45
+ `Visit ${(0, picocolors_1.bold)((0, picocolors_1.blue)(config_1.websiteUrl + '/usage'))} to add more credits and continue coding.`,
46
+ ].join('\n'),
47
+ threshold: 100,
48
+ },
49
+ [constants_2.UserState.CRITICAL]: {
50
+ message: (credits) => [
51
+ (0, picocolors_1.yellow)(`\n🪫 Only ${(0, picocolors_1.bold)((0, string_1.pluralize)(credits, 'credit'))} remaining!`),
52
+ (0, picocolors_1.yellow)(`Visit ${(0, picocolors_1.bold)(config_1.websiteUrl + '/usage')} to add more credits.`),
53
+ ].join('\n'),
54
+ threshold: 85,
55
+ },
56
+ [constants_2.UserState.ATTENTION_NEEDED]: {
57
+ message: (credits) => [
58
+ (0, picocolors_1.yellow)(`\n⚠️ ${(0, picocolors_1.bold)((0, string_1.pluralize)(credits, 'credit'))} remaining. Consider topping up soon.`),
59
+ ].join('\n'),
60
+ threshold: 75,
61
+ },
62
+ [constants_2.UserState.GOOD_STANDING]: {
63
+ message: () => '',
64
+ threshold: 0,
65
+ },
66
+ };
51
67
  class Client {
52
68
  webSocket;
53
69
  returnControlToUser;
70
+ reconnectWhenNextIdle;
54
71
  fingerprintId;
55
72
  costMode;
73
+ hadFileChanges = false;
74
+ git;
75
+ rl;
76
+ responseComplete = false;
77
+ pendingTopUpMessageAmount = null;
78
+ oneTimeFlags = Object.fromEntries(constants_2.ONE_TIME_LABELS.map((tag) => [tag, false]));
79
+ usageData = {
80
+ usage: 0,
81
+ remainingBalance: 0,
82
+ balanceBreakdown: undefined,
83
+ next_quota_reset: null,
84
+ nextMonthlyGrant: 0,
85
+ };
56
86
  fileContext;
57
87
  lastChanges = [];
58
88
  agentState;
59
89
  originalFileVersions = {};
90
+ creditsByPromptId = {};
60
91
  user;
61
92
  lastWarnedPct = 0;
62
- usage = 0;
63
- limit = 0;
64
- subscription_active = false;
65
- lastRequestCredits = 0;
66
- sessionCreditsUsed = 0;
67
- nextQuotaReset = null;
68
- hadFileChanges = false;
69
- git;
70
- rl;
93
+ nextMonthlyGrant = 0;
94
+ storedApiKeyTypes = [];
71
95
  lastToolResults = [];
72
- pendingRequestId = null;
73
- constructor(websocketUrl, onWebSocketError, onWebSocketReconnect, returnControlToUser, costMode, git, rl) {
96
+ model;
97
+ constructor(websocketUrl, onWebSocketError, onWebSocketReconnect, returnControlToUser, reconnectWhenNextIdle, costMode, git, rl, model) {
74
98
  this.costMode = costMode;
99
+ this.model = model;
75
100
  this.git = git;
76
101
  this.webSocket = new websocket_client_1.APIRealtimeClient(websocketUrl, onWebSocketError, onWebSocketReconnect);
77
102
  this.user = this.getUser();
78
- this.getFingerprintId();
103
+ this.initFingerprintId();
79
104
  this.returnControlToUser = returnControlToUser;
105
+ this.reconnectWhenNextIdle = reconnectWhenNextIdle;
80
106
  this.rl = rl;
81
107
  }
82
108
  async exit() {
@@ -89,25 +115,104 @@ class Client {
89
115
  this.agentState = (0, agent_state_1.getInitialAgentState)(projectFileContext);
90
116
  this.fileContext = projectFileContext;
91
117
  }
92
- async getFingerprintId() {
93
- if (this.fingerprintId) {
94
- return this.fingerprintId;
118
+ initFingerprintId() {
119
+ if (!this.fingerprintId) {
120
+ this.fingerprintId = this.user?.fingerprintId ?? (0, fingerprint_1.calculateFingerprint)();
95
121
  }
96
- this.fingerprintId =
97
- this.user?.fingerprintId ?? (await (0, fingerprint_1.calculateFingerprint)());
98
122
  return this.fingerprintId;
99
123
  }
100
124
  getUser() {
101
- if (!fs.existsSync(credentials_1.CREDENTIALS_PATH)) {
125
+ if (!(0, fs_1.existsSync)(credentials_1.CREDENTIALS_PATH)) {
102
126
  return;
103
127
  }
104
- const credentialsFile = fs.readFileSync(credentials_1.CREDENTIALS_PATH, 'utf8');
128
+ const credentialsFile = (0, fs_1.readFileSync)(credentials_1.CREDENTIALS_PATH, 'utf8');
105
129
  const user = (0, credentials_1.userFromJson)(credentialsFile);
106
130
  return user;
107
131
  }
108
132
  async connect() {
109
133
  await this.webSocket.connect();
110
134
  this.setupSubscriptions();
135
+ await this.fetchStoredApiKeyTypes();
136
+ }
137
+ async fetchStoredApiKeyTypes() {
138
+ if (!this.user || !this.user.authToken) {
139
+ return;
140
+ }
141
+ const TIMEOUT_MS = 5_000;
142
+ // try {
143
+ // const timeoutPromise = new Promise<Response>((_, reject) => {
144
+ // setTimeout(() => reject(new Error('Request timed out')), TIMEOUT_MS)
145
+ // })
146
+ // const fetchPromise = fetch(
147
+ // `${process.env.NEXT_PUBLIC_APP_URL}/api/api-keys`,
148
+ // {
149
+ // method: 'GET',
150
+ // headers: {
151
+ // 'Content-Type': 'application/json',
152
+ // Cookie: `next-auth.session-token=${this.user.authToken}`,
153
+ // Authorization: `Bearer ${this.user.authToken}`,
154
+ // },
155
+ // }
156
+ // )
157
+ // const response = await Promise.race([fetchPromise, timeoutPromise])
158
+ // if (response.ok) {
159
+ // const { keyTypes } = await response.json()
160
+ // this.storedApiKeyTypes = keyTypes as ApiKeyType[]
161
+ // } else {
162
+ // this.storedApiKeyTypes = []
163
+ // }
164
+ // } catch (error) {
165
+ // if (process.env.NODE_ENV !== 'production') {
166
+ // console.error(
167
+ // 'Error fetching stored API key types (is there something else on port 3000?):',
168
+ // error
169
+ // )
170
+ // }
171
+ // this.storedApiKeyTypes = []
172
+ // }
173
+ this.storedApiKeyTypes = [];
174
+ }
175
+ async handleAddApiKey(keyType, apiKey) {
176
+ if (!this.user || !this.user.authToken) {
177
+ console.log((0, picocolors_1.yellow)("Please log in first using 'login'."));
178
+ this.returnControlToUser();
179
+ return;
180
+ }
181
+ const readableKeyType = constants_1.READABLE_NAME[keyType];
182
+ spinner_1.Spinner.get().start();
183
+ try {
184
+ const response = await fetch(`${process.env.NEXT_PUBLIC_APP_URL}/api/api-keys`, {
185
+ method: 'POST',
186
+ headers: {
187
+ 'Content-Type': 'application/json',
188
+ Cookie: `next-auth.session-token=${this.user.authToken}`,
189
+ },
190
+ body: JSON.stringify({
191
+ keyType,
192
+ apiKey,
193
+ authToken: this.user.authToken,
194
+ }),
195
+ });
196
+ spinner_1.Spinner.get().stop();
197
+ const respJson = await response.json();
198
+ if (response.ok) {
199
+ console.log((0, picocolors_1.green)(`Successfully added ${readableKeyType} API key.`));
200
+ if (!this.storedApiKeyTypes.includes(keyType)) {
201
+ this.storedApiKeyTypes.push(keyType);
202
+ }
203
+ }
204
+ else {
205
+ throw new Error(respJson.message);
206
+ }
207
+ }
208
+ catch (e) {
209
+ spinner_1.Spinner.get().stop();
210
+ const error = e;
211
+ console.error((0, picocolors_1.red)('Error adding API key: ' + error.message));
212
+ }
213
+ finally {
214
+ this.returnControlToUser();
215
+ }
111
216
  }
112
217
  async handleReferralCode(referralCode) {
113
218
  if (this.user) {
@@ -127,7 +232,7 @@ class Client {
127
232
  if (redeemReferralResp.ok) {
128
233
  console.log([
129
234
  (0, picocolors_1.green)(`Noice, you've earned an extra ${respJson.credits_redeemed} credits!`),
130
- `(pssst: you can also refer new users and earn ${constants_1.CREDITS_REFERRAL_BONUS} credits for each referral at: ${process.env.NEXT_PUBLIC_APP_URL}/referrals)`,
235
+ `(pssst: you can also refer new users and earn ${constants_2.CREDITS_REFERRAL_BONUS} credits for each referral at: ${process.env.NEXT_PUBLIC_APP_URL}/referrals)`,
131
236
  ].join('\n'));
132
237
  this.getUsage();
133
238
  }
@@ -148,7 +253,7 @@ class Client {
148
253
  async logout() {
149
254
  if (this.user) {
150
255
  try {
151
- const response = await fetch(`${config_1.backendUrl}/api/auth/cli/logout`, {
256
+ const response = await fetch(`${config_1.websiteUrl}/api/auth/cli/logout`, {
152
257
  method: 'POST',
153
258
  headers: { 'Content-Type': 'application/json' },
154
259
  body: JSON.stringify({
@@ -163,9 +268,18 @@ class Client {
163
268
  console.error((0, picocolors_1.red)('Failed to log out: ' + error));
164
269
  }
165
270
  try {
166
- fs.unlinkSync(credentials_1.CREDENTIALS_PATH);
271
+ (0, fs_1.unlinkSync)(credentials_1.CREDENTIALS_PATH);
167
272
  console.log(`You (${this.user.name}) have been logged out.`);
168
273
  this.user = undefined;
274
+ this.pendingTopUpMessageAmount = null;
275
+ this.usageData = {
276
+ usage: 0,
277
+ remainingBalance: 0,
278
+ balanceBreakdown: undefined,
279
+ next_quota_reset: null,
280
+ nextMonthlyGrant: 0,
281
+ };
282
+ this.oneTimeFlags = Object.fromEntries(constants_2.ONE_TIME_LABELS.map((tag) => [tag, false]));
169
283
  }
170
284
  catch (error) {
171
285
  console.error('Error removing credentials file:', error);
@@ -183,11 +297,11 @@ class Client {
183
297
  return;
184
298
  }
185
299
  try {
186
- const response = await fetch(`${config_1.backendUrl}/api/auth/cli/code`, {
300
+ const response = await fetch(`${config_1.websiteUrl}/api/auth/cli/code`, {
187
301
  method: 'POST',
188
302
  headers: { 'Content-Type': 'application/json' },
189
303
  body: JSON.stringify({
190
- fingerprintId: await this.getFingerprintId(),
304
+ fingerprintId: await this.fingerprintId,
191
305
  referralCode,
192
306
  }),
193
307
  });
@@ -197,7 +311,7 @@ class Client {
197
311
  this.returnControlToUser();
198
312
  return;
199
313
  }
200
- const { loginUrl, fingerprintHash } = await response.json();
314
+ const { loginUrl, fingerprintHash, expiresAt } = await response.json();
201
315
  const responseToUser = [
202
316
  '\n',
203
317
  `Press ${(0, picocolors_1.blue)('ENTER')} to open your browser and finish logging in...`,
@@ -207,7 +321,7 @@ class Client {
207
321
  this.rl.once('line', () => {
208
322
  if (shouldRequestLogin) {
209
323
  (0, child_process_1.spawn)(`open ${loginUrl}`, { shell: true });
210
- console.log('Done. If nothing happened, copy and paste this link into your browser:');
324
+ console.log("Opened a browser window to log you in! If it doesn't open automatically, you can click this link:");
211
325
  console.log();
212
326
  console.log((0, picocolors_1.blue)((0, picocolors_1.bold)((0, picocolors_1.underline)(loginUrl))));
213
327
  }
@@ -226,7 +340,7 @@ class Client {
226
340
  return;
227
341
  }
228
342
  try {
229
- const statusResponse = await fetch(`${config_1.backendUrl}/api/auth/cli/status?fingerprintId=${await this.getFingerprintId()}&fingerprintHash=${fingerprintHash}`);
343
+ const statusResponse = await fetch(`${config_1.websiteUrl}/api/auth/cli/status?fingerprintId=${await this.fingerprintId}&fingerprintHash=${fingerprintHash}&expiresAt=${expiresAt}`);
230
344
  if (!statusResponse.ok) {
231
345
  if (statusResponse.status !== 401) {
232
346
  // Ignore 401s during polling
@@ -239,16 +353,17 @@ class Client {
239
353
  shouldRequestLogin = false;
240
354
  this.user = user;
241
355
  const credentialsPathDir = path_1.default.dirname(credentials_1.CREDENTIALS_PATH);
242
- fs.mkdirSync(credentialsPathDir, { recursive: true });
243
- fs.writeFileSync(credentials_1.CREDENTIALS_PATH, JSON.stringify({ default: user }));
356
+ (0, fs_1.mkdirSync)(credentialsPathDir, { recursive: true });
357
+ (0, fs_1.writeFileSync)(credentials_1.CREDENTIALS_PATH, JSON.stringify({ default: user }));
244
358
  const referralLink = `${process.env.NEXT_PUBLIC_APP_URL}/referrals`;
245
359
  const responseToUser = [
246
360
  'Authentication successful! 🎉',
247
361
  (0, picocolors_1.bold)(`Hey there, ${user.name}.`),
248
- `Refer new users and earn ${constants_1.CREDITS_REFERRAL_BONUS} credits per month: ${(0, picocolors_1.blueBright)(referralLink)}`,
362
+ `Refer new users and earn ${constants_2.CREDITS_REFERRAL_BONUS} credits per month: ${(0, picocolors_1.blueBright)(referralLink)}`,
249
363
  ];
250
364
  console.log('\n' + responseToUser.join('\n'));
251
365
  this.lastWarnedPct = 0;
366
+ this.oneTimeFlags = Object.fromEntries(constants_2.ONE_TIME_LABELS.map((tag) => [tag, false]));
252
367
  (0, menu_1.displayGreeting)(this.costMode, null);
253
368
  clearInterval(pollInterval);
254
369
  this.returnControlToUser();
@@ -264,19 +379,25 @@ class Client {
264
379
  this.returnControlToUser();
265
380
  }
266
381
  }
267
- setUsage({ usage, limit, subscription_active, next_quota_reset, session_credits_used, }) {
268
- this.usage = usage;
269
- this.limit = limit;
270
- this.subscription_active = subscription_active;
271
- this.nextQuotaReset = next_quota_reset;
272
- if (!!session_credits_used && !this.pendingRequestId) {
273
- this.lastRequestCredits = Math.max(session_credits_used - this.sessionCreditsUsed, 0);
274
- this.sessionCreditsUsed = session_credits_used;
275
- }
382
+ setUsage(usageData) {
383
+ this.usageData = usageData;
384
+ }
385
+ reconnect() {
386
+ this.webSocket.forceReconnect();
276
387
  }
277
388
  setupSubscriptions() {
278
389
  this.webSocket.subscribe('action-error', (action) => {
279
- console.error(['', (0, picocolors_1.red)(`Error: ${action.message}`)].join('\n'));
390
+ if (action.error === 'Insufficient credits') {
391
+ console.error(['', (0, picocolors_1.red)(`Error: ${action.message}`)].join('\n'));
392
+ console.error(`Visit ${(0, picocolors_1.blue)((0, picocolors_1.bold)(process.env.NEXT_PUBLIC_APP_URL + '/usage'))} to add credits.`);
393
+ }
394
+ else if (action.error === 'Auto top-up disabled') {
395
+ console.error(['', (0, picocolors_1.red)(`Error: ${action.message}`)].join('\n'));
396
+ console.error((0, picocolors_1.yellow)(`Visit ${(0, picocolors_1.blue)((0, picocolors_1.bold)(process.env.NEXT_PUBLIC_APP_URL + '/usage'))} to update your payment settings.`));
397
+ }
398
+ else {
399
+ console.error(['', (0, picocolors_1.red)(`Error: ${action.message}`)].join('\n'));
400
+ }
280
401
  this.returnControlToUser();
281
402
  return;
282
403
  });
@@ -295,45 +416,61 @@ class Client {
295
416
  console.warn((0, picocolors_1.yellow)(`\nThere's a new version of Codebuff! Please update to ensure proper functionality.\nUpdate now by running: npm install -g codebuff`));
296
417
  }
297
418
  });
298
- this.webSocket.subscribe('usage-response', (action) => {
299
- const parsedAction = actions_1.UsageReponseSchema.safeParse(action);
419
+ this.webSocket.subscribe('message-cost-response', (action) => {
420
+ const parsedAction = actions_1.MessageCostResponseSchema.safeParse(action);
300
421
  if (!parsedAction.success)
301
422
  return;
302
- const a = parsedAction.data;
303
- console.log();
304
- console.log((0, picocolors_1.green)((0, picocolors_1.underline)(`Codebuff usage:`)), `${a.usage} / ${a.limit} credits`);
305
- this.setUsage(a);
306
- this.returnControlToUser();
423
+ const response = parsedAction.data;
424
+ // Store credits used for this prompt
425
+ if (!this.creditsByPromptId[response.promptId]) {
426
+ this.creditsByPromptId[response.promptId] = [];
427
+ }
428
+ this.creditsByPromptId[response.promptId].push(response.credits);
307
429
  });
308
- }
309
- showUsageWarning() {
310
- const errorCopy = [
311
- this.user
312
- ? `Visit ${(0, picocolors_1.blue)((0, picocolors_1.bold)(process.env.NEXT_PUBLIC_APP_URL + '/pricing'))} to upgrade – or refer a new user and earn ${constants_1.CREDITS_REFERRAL_BONUS} credits per month: ${(0, picocolors_1.blue)((0, picocolors_1.bold)(process.env.NEXT_PUBLIC_APP_URL + '/referrals'))}`
313
- : (0, picocolors_1.green)('Type "login" below to sign up and get more credits!'),
314
- ].join('\n');
315
- const pct = (0, ts_pattern_1.match)(Math.floor((this.usage / this.limit) * 100))
316
- .with(ts_pattern_1.P.number.gte(100), () => 100)
317
- .with(ts_pattern_1.P.number.gte(75), () => 75)
318
- .otherwise(() => 0);
319
- if (pct >= 100) {
320
- this.lastWarnedPct = 100;
321
- if (!this.subscription_active) {
322
- console.error([(0, picocolors_1.red)('You have reached your monthly usage limit.'), errorCopy].join('\n'));
430
+ this.webSocket.subscribe('usage-response', (action) => {
431
+ const parsedAction = actions_1.UsageReponseSchema.safeParse(action);
432
+ if (!parsedAction.success) {
433
+ console.error((0, picocolors_1.red)('Received invalid usage data from server:'), parsedAction.error.errors);
323
434
  return;
324
435
  }
325
- if (this.subscription_active && this.lastWarnedPct < 100) {
326
- console.warn((0, picocolors_1.yellow)(`You have exceeded your monthly quota, but feel free to keep using Codebuff! We'll continue to charge you for the overage until your next billing cycle. See ${process.env.NEXT_PUBLIC_APP_URL}/usage for more details.`));
327
- return;
436
+ this.setUsage(parsedAction.data);
437
+ // Store auto-topup info if it occurred
438
+ if (parsedAction.data.autoTopupAdded) {
439
+ this.pendingTopUpMessageAmount = parsedAction.data.autoTopupAdded;
328
440
  }
441
+ // Only show warning if the response is complete
442
+ if (this.responseComplete) {
443
+ this.showUsageWarning();
444
+ }
445
+ });
446
+ // Used to handle server restarts gracefully
447
+ this.webSocket.subscribe('request-reconnect', () => {
448
+ this.reconnectWhenNextIdle();
449
+ });
450
+ }
451
+ showUsageWarning() {
452
+ // Determine user state based on login status and credit balance
453
+ const state = (0, ts_pattern_1.match)({
454
+ isLoggedIn: !!this.user,
455
+ credits: this.usageData.remainingBalance,
456
+ })
457
+ .with({ isLoggedIn: false }, () => constants_2.UserState.LOGGED_OUT)
458
+ .with({ credits: ts_pattern_1.P.number.gte(100) }, () => constants_2.UserState.GOOD_STANDING)
459
+ .with({ credits: ts_pattern_1.P.number.gte(20) }, () => constants_2.UserState.ATTENTION_NEEDED)
460
+ .with({ credits: ts_pattern_1.P.number.gte(1) }, () => constants_2.UserState.CRITICAL)
461
+ .otherwise(() => constants_2.UserState.DEPLETED);
462
+ const config = WARNING_CONFIG[state];
463
+ // Reset warning percentage if in good standing
464
+ if (state === constants_2.UserState.GOOD_STANDING) {
465
+ this.lastWarnedPct = 0;
466
+ return;
329
467
  }
330
- if (pct > 0 && pct > this.lastWarnedPct) {
331
- console.warn([
332
- '',
333
- (0, picocolors_1.yellow)(`You have used over ${pct}% of your monthly usage limit (${this.usage}/${this.limit} credits).`),
334
- errorCopy,
335
- ].join('\n'));
336
- this.lastWarnedPct = pct;
468
+ // Show warning if we haven't warned at this threshold yet
469
+ if (this.lastWarnedPct < config.threshold) {
470
+ const message = config.message(this.usageData.remainingBalance);
471
+ console.warn(message);
472
+ this.lastWarnedPct = config.threshold;
473
+ this.returnControlToUser();
337
474
  }
338
475
  }
339
476
  async generateCommitMessage(stagedChanges) {
@@ -344,7 +481,7 @@ class Client {
344
481
  });
345
482
  this.webSocket.sendAction({
346
483
  type: 'generate-commit-message',
347
- fingerprintId: await this.getFingerprintId(),
484
+ fingerprintId: await this.fingerprintId,
348
485
  authToken: this.user?.authToken,
349
486
  stagedChanges,
350
487
  });
@@ -355,7 +492,6 @@ class Client {
355
492
  throw new Error('Agent state not initialized');
356
493
  }
357
494
  const userInputId = `mc-input-` + Math.random().toString(36).substring(2, 15);
358
- this.pendingRequestId = userInputId;
359
495
  const { responsePromise, stopResponse } = this.subscribeToResponse((chunk) => {
360
496
  spinner_1.Spinner.get().stop();
361
497
  process.stdout.write(chunk);
@@ -363,16 +499,26 @@ class Client {
363
499
  spinner_1.Spinner.get().stop();
364
500
  process.stdout.write((0, picocolors_1.green)((0, picocolors_1.underline)('\nCodebuff') + ':') + ' ');
365
501
  }, prompt);
502
+ const urls = (0, web_scraper_2.parseUrlsFromContent)(prompt);
503
+ const scrapedBlocks = await (0, web_scraper_1.getScrapedContentBlocks)(urls);
504
+ const scrapedContent = scrapedBlocks.length > 0 ? scrapedBlocks.join('\n\n') + '\n\n' : '';
505
+ // Append process updates to existing tool results
506
+ const toolResults = (0, array_1.buildArray)(...(this.lastToolResults || []), ...(0, background_process_manager_1.getBackgroundProcessUpdates)(), scrapedContent && {
507
+ id: 'scraped-content',
508
+ name: 'web-scraper',
509
+ result: scrapedContent,
510
+ });
366
511
  spinner_1.Spinner.get().start();
367
512
  this.webSocket.sendAction({
368
513
  type: 'prompt',
369
514
  promptId: userInputId,
370
515
  prompt,
371
516
  agentState: this.agentState,
372
- toolResults: this.lastToolResults,
373
- fingerprintId: await this.getFingerprintId(),
517
+ toolResults,
518
+ fingerprintId: await this.fingerprintId,
374
519
  authToken: this.user?.authToken,
375
520
  costMode: this.costMode,
521
+ model: this.model,
376
522
  });
377
523
  return {
378
524
  responsePromise,
@@ -393,11 +539,11 @@ class Client {
393
539
  });
394
540
  const stopResponse = () => {
395
541
  responseStopped = true;
396
- // Only unsubscribe from chunks, keep listening for final credits
397
542
  unsubscribeChunks();
543
+ unsubscribeComplete();
398
544
  const assistantMessage = {
399
- role: 'assistant',
400
- content: responseBuffer + '[RESPONSE_CANCELED_BY_USER]',
545
+ role: 'user',
546
+ content: `Received output:\n${responseBuffer}[RESPONSE_CANCELED_BY_USER]`,
401
547
  };
402
548
  // Update the agent state with just the assistant's response
403
549
  const { messageHistory } = this.agentState;
@@ -424,6 +570,21 @@ class Client {
424
570
  if (a.userInputId !== userInputId)
425
571
  return;
426
572
  const { chunk } = a;
573
+ const trimmed = chunk.trim();
574
+ for (const tag of constants_2.ONE_TIME_TAGS) {
575
+ if (trimmed.startsWith(`<${tag}>`) && trimmed.endsWith(`</${tag}>`)) {
576
+ if (this.oneTimeFlags[tag]) {
577
+ return;
578
+ }
579
+ spinner_1.Spinner.get().stop();
580
+ const warningMessage = trimmed
581
+ .replace(`<${tag}>`, '')
582
+ .replace(`</${tag}>`, '');
583
+ console.warn((0, picocolors_1.yellow)(`\n${warningMessage}`));
584
+ this.oneTimeFlags[tag] = true;
585
+ return;
586
+ }
587
+ }
427
588
  if (chunk && chunk.trim()) {
428
589
  if (!streamStarted && chunk.trim()) {
429
590
  streamStarted = true;
@@ -444,45 +605,51 @@ class Client {
444
605
  if (action.promptId !== userInputId)
445
606
  return;
446
607
  const a = parsedAction.data;
447
- // Only process usage data if this is our pending request
448
- const usageData = actions_1.UsageReponseSchema.omit({ type: true }).safeParse(a);
449
- if (usageData.success) {
450
- this.pendingRequestId = null;
451
- this.setUsage(usageData.data);
452
- }
453
- if (responseStopped) {
454
- unsubscribeComplete();
455
- xmlStreamParser.end();
456
- return;
457
- }
458
- this.agentState = a.agentState;
459
- spinner_1.Spinner.get().stop();
460
608
  let isComplete = false;
609
+ spinner_1.Spinner.get().stop();
610
+ this.agentState = a.agentState;
461
611
  const toolResults = [...a.toolResults];
462
612
  for (const toolCall of a.toolCalls) {
463
- if (toolCall.name === 'end_turn') {
464
- isComplete = true;
465
- continue;
466
- }
467
- if (toolCall.name === 'write_file') {
468
- // Save lastChanges for `diff` command
469
- this.lastChanges.push(actions_1.FileChangeSchema.parse(toolCall.parameters));
470
- this.hadFileChanges = true;
613
+ try {
614
+ if (toolCall.name === 'end_turn') {
615
+ this.responseComplete = true;
616
+ isComplete = true;
617
+ continue;
618
+ }
619
+ if (toolCall.name === 'write_file') {
620
+ // Save lastChanges for `diff` command
621
+ this.lastChanges.push(actions_1.FileChangeSchema.parse(toolCall.parameters));
622
+ this.hadFileChanges = true;
623
+ }
624
+ if (toolCall.name === 'run_terminal_command' &&
625
+ toolCall.parameters.mode === 'user') {
626
+ // Special case: when terminal command is run it as a user command, then no need to reprompt assistant.
627
+ this.responseComplete = true;
628
+ isComplete = true;
629
+ }
630
+ if (toolCall.name === 'run_terminal_command' &&
631
+ toolCall.parameters.mode === 'assistant' &&
632
+ toolCall.parameters.process_type === 'BACKGROUND') {
633
+ this.oneTimeFlags[constants_2.SHOULD_ASK_CONFIG] = true;
634
+ }
635
+ const toolResult = await (0, tool_handlers_1.handleToolCall)(toolCall, (0, project_files_1.getProjectRoot)());
636
+ toolResults.push(toolResult);
471
637
  }
472
- if (toolCall.name === 'run_terminal_command' &&
473
- toolCall.parameters.mode === 'user') {
474
- // Special case: when terminal command is run it as a user command, then no need to reprompt assistant.
475
- isComplete = true;
638
+ catch (error) {
639
+ console.error((0, picocolors_1.red)(`Error parsing tool call ${toolCall.name}:\n${error}`));
476
640
  }
477
- const toolResult = await (0, tool_handlers_1.handleToolCall)(toolCall, (0, project_files_1.getProjectRoot)());
478
- toolResults.push(toolResult);
641
+ }
642
+ if (toolResults.length > 0 ||
643
+ a.toolCalls.some((call) => call.name === 'end_turn')) {
644
+ console.log();
479
645
  }
480
646
  // If we had any file changes, update the project context
481
647
  if (this.hadFileChanges) {
482
648
  this.fileContext = await (0, project_files_1.getProjectFileContext)((0, project_files_1.getProjectRoot)(), {});
483
649
  }
484
650
  if (!isComplete) {
485
- spinner_1.Spinner.get().start();
651
+ // Append process updates to existing tool results
652
+ toolResults.push(...(0, background_process_manager_1.getBackgroundProcessUpdates)());
486
653
  // Continue the prompt with the tool results.
487
654
  this.webSocket.sendAction({
488
655
  type: 'prompt',
@@ -490,41 +657,117 @@ class Client {
490
657
  prompt: undefined,
491
658
  agentState: this.agentState,
492
659
  toolResults,
493
- fingerprintId: await this.getFingerprintId(),
660
+ fingerprintId: await this.fingerprintId,
494
661
  authToken: this.user?.authToken,
495
662
  costMode: this.costMode,
663
+ model: this.model,
496
664
  });
497
665
  return;
498
666
  }
499
667
  this.lastToolResults = toolResults;
500
668
  xmlStreamParser.end();
669
+ askConfig: if (this.oneTimeFlags[constants_2.SHOULD_ASK_CONFIG] &&
670
+ !this.oneTimeFlags[constants_2.ASKED_CONFIG]) {
671
+ this.oneTimeFlags[constants_2.ASKED_CONFIG] = true;
672
+ if ((0, fs_1.existsSync)(path_1.default.join((0, project_files_1.getProjectRoot)(), constants_3.codebuffConfigFile))) {
673
+ break askConfig;
674
+ }
675
+ console.log((0, picocolors_1.yellow)(`✨ Recommended: run the 'init' command in order to create a configuration file!
676
+
677
+ If you would like background processes (like this one) to run automatically whenever codebuff starts, creating a ${constants_3.codebuffConfigFile} config file can improve your workflow.
678
+ Go to https://www.codebuff.com/config for more information.
679
+ `));
680
+ }
501
681
  if (this.agentState) {
502
682
  (0, chat_storage_1.setMessages)(this.agentState.messageHistory);
503
683
  }
504
- this.showUsageWarning();
684
+ // Show total credits used for this prompt if significant
685
+ const credits = this.creditsByPromptId[userInputId]?.reduce((a, b) => a + b, 0) ?? 0;
686
+ if (credits >= constants_2.REQUEST_CREDIT_SHOW_THRESHOLD) {
687
+ console.log(`${(0, string_1.pluralize)(credits, 'credit')} used for this request.`);
688
+ }
689
+ // Show auto top-up success message if it occurred during this response
690
+ if (this.pendingTopUpMessageAmount) {
691
+ console.log((0, picocolors_1.green)(`Auto top-up successful! ${this.pendingTopUpMessageAmount.toLocaleString()} credits added.`));
692
+ this.pendingTopUpMessageAmount = null;
693
+ }
505
694
  if (this.hadFileChanges) {
506
- const latestCheckpoint = checkpoint_manager_1.checkpointManager.getLatestCheckpoint();
507
- const displayedCheckpointString = latestCheckpoint === null
508
- ? ''
509
- : ` or "checkpoint ${latestCheckpoint.id}" to revert`;
510
- console.log(`\nComplete! Type "diff" to review changes${displayedCheckpointString}.`);
695
+ let checkpointAddendum = '';
696
+ try {
697
+ checkpointAddendum = ` or "checkpoint ${checkpoint_manager_1.checkpointManager.getLatestCheckpoint().id}" to revert`;
698
+ }
699
+ catch (error) {
700
+ // No latest checkpoint, don't show addendum
701
+ }
702
+ console.log(`\nComplete! Type "diff" to review changes${checkpointAddendum}.`);
511
703
  this.hadFileChanges = false;
704
+ this.returnControlToUser();
512
705
  }
513
706
  unsubscribeChunks();
514
707
  unsubscribeComplete();
515
708
  resolveResponse({ ...a, wasStoppedByUser: false });
516
709
  });
710
+ // Reset flags at the start of each response
711
+ this.responseComplete = false;
517
712
  return {
518
713
  responsePromise,
519
714
  stopResponse,
520
715
  };
521
716
  }
522
717
  async getUsage() {
523
- this.webSocket.sendAction({
524
- type: 'usage',
525
- fingerprintId: await this.getFingerprintId(),
526
- authToken: this.user?.authToken,
527
- });
718
+ try {
719
+ const response = await fetch(`${config_1.backendUrl}/api/usage`, {
720
+ method: 'POST',
721
+ headers: {
722
+ 'Content-Type': 'application/json',
723
+ },
724
+ body: JSON.stringify({
725
+ fingerprintId: await this.fingerprintId,
726
+ authToken: this.user?.authToken,
727
+ }),
728
+ });
729
+ const data = await response.json();
730
+ // Use zod schema to validate response
731
+ const parsedResponse = actions_1.UsageReponseSchema.parse(data);
732
+ if (data.type === 'action-error') {
733
+ console.error((0, picocolors_1.red)(data.message));
734
+ return;
735
+ }
736
+ this.setUsage(parsedResponse);
737
+ const usageLink = `${config_1.websiteUrl}/usage`;
738
+ const remainingColor = this.usageData.remainingBalance <= 0
739
+ ? picocolors_1.red
740
+ : this.usageData.remainingBalance <= LOW_BALANCE_THRESHOLD
741
+ ? picocolors_1.red
742
+ : picocolors_1.green;
743
+ const totalCreditsUsedThisSession = Object.values(this.creditsByPromptId)
744
+ .flat()
745
+ .reduce((sum, credits) => sum + credits, 0);
746
+ console.log(`Session usage: ${totalCreditsUsedThisSession.toLocaleString()}. Credits Remaining: ${remainingColor(this.usageData.remainingBalance.toLocaleString())}`);
747
+ if (this.usageData.next_quota_reset) {
748
+ const resetDate = new Date(this.usageData.next_quota_reset);
749
+ const today = new Date();
750
+ const isToday = resetDate.toDateString() === today.toDateString();
751
+ const dateDisplay = isToday
752
+ ? resetDate.toLocaleString() // Show full date and time for today
753
+ : resetDate.toLocaleDateString(); // Just show date otherwise
754
+ console.log(`Free credits will renew on ${dateDisplay}. Details: ${(0, picocolors_1.underline)((0, picocolors_1.blue)(usageLink))}`);
755
+ }
756
+ this.showUsageWarning();
757
+ }
758
+ catch (error) {
759
+ console.error((0, picocolors_1.red)(`Error checking usage: Please reach out to ${process.env.NEXT_PUBLIC_SUPPORT_EMAIL} for help.`));
760
+ // Check if it's a ZodError for more specific feedback
761
+ if (error instanceof zod_1.z.ZodError) {
762
+ console.error((0, picocolors_1.red)('Data validation failed:'), error.errors);
763
+ }
764
+ else {
765
+ console.error(error);
766
+ }
767
+ }
768
+ finally {
769
+ this.returnControlToUser();
770
+ }
528
771
  }
529
772
  async warmContextCache() {
530
773
  const fileContext = await (0, project_files_1.getProjectFileContext)((0, project_files_1.getProjectRoot)(), {});
@@ -532,18 +775,16 @@ class Client {
532
775
  const parsedAction = actions_1.InitResponseSchema.safeParse(a);
533
776
  if (!parsedAction.success)
534
777
  return;
778
+ // Set initial usage data from the init response
535
779
  this.setUsage(parsedAction.data);
536
780
  });
537
- this.webSocket
538
- .sendAction({
781
+ this.webSocket.sendAction({
539
782
  type: 'init',
540
- fingerprintId: await this.getFingerprintId(),
783
+ fingerprintId: await this.fingerprintId,
541
784
  authToken: this.user?.authToken,
542
785
  fileContext,
543
- })
544
- .catch((e) => {
545
- // console.error('Error warming context cache', e)
546
786
  });
787
+ await this.fetchStoredApiKeyTypes();
547
788
  }
548
789
  }
549
790
  exports.Client = Client;