codebuff 1.0.219 → 1.0.220

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 (121) hide show
  1. package/dist/checkpoints/checkpoint-manager.d.ts +1 -1
  2. package/dist/checkpoints/checkpoint-manager.js +8 -6
  3. package/dist/checkpoints/checkpoint-manager.js.map +1 -1
  4. package/dist/cli-handlers/api-key.d.ts +25 -0
  5. package/dist/cli-handlers/api-key.js +66 -0
  6. package/dist/cli-handlers/api-key.js.map +1 -0
  7. package/dist/cli-handlers/checkpoint.d.ts +18 -0
  8. package/dist/cli-handlers/checkpoint.js +195 -0
  9. package/dist/cli-handlers/checkpoint.js.map +1 -0
  10. package/dist/cli-handlers/diff.d.ts +2 -0
  11. package/dist/cli-handlers/diff.js +31 -0
  12. package/dist/cli-handlers/diff.js.map +1 -0
  13. package/dist/cli-handlers/easter-egg.d.ts +1 -0
  14. package/dist/cli-handlers/easter-egg.js +126 -0
  15. package/dist/cli-handlers/easter-egg.js.map +1 -0
  16. package/dist/cli.d.ts +0 -18
  17. package/dist/cli.js +70 -397
  18. package/dist/cli.js.map +1 -1
  19. package/dist/client.d.ts +11 -11
  20. package/dist/client.js +123 -45
  21. package/dist/client.js.map +1 -1
  22. package/dist/code-map/tsconfig.tsbuildinfo +1 -1
  23. package/dist/common/actions.d.ts +288 -264
  24. package/dist/common/actions.js +10 -4
  25. package/dist/common/actions.js.map +1 -1
  26. package/dist/common/advanced-analyzer.d.ts +19 -0
  27. package/dist/common/advanced-analyzer.js +140 -0
  28. package/dist/common/advanced-analyzer.js.map +1 -0
  29. package/dist/common/billing/auto-topup.d.ts +8 -0
  30. package/dist/common/billing/auto-topup.js +192 -0
  31. package/dist/common/billing/auto-topup.js.map +1 -0
  32. package/dist/common/billing/balance-calculator.d.ts +55 -0
  33. package/dist/common/billing/balance-calculator.js +207 -0
  34. package/dist/common/billing/balance-calculator.js.map +1 -0
  35. package/dist/common/billing/check-auto-topup.d.ts +12 -0
  36. package/dist/common/billing/check-auto-topup.js +50 -0
  37. package/dist/common/billing/check-auto-topup.js.map +1 -0
  38. package/dist/common/billing/conversion.d.ts +9 -0
  39. package/dist/common/billing/conversion.js +20 -0
  40. package/dist/common/billing/conversion.js.map +1 -0
  41. package/dist/common/billing/credit-check.d.ts +8 -0
  42. package/dist/common/billing/credit-check.js +45 -0
  43. package/dist/common/billing/credit-check.js.map +1 -0
  44. package/dist/common/billing/credit-conversion.d.ts +24 -0
  45. package/dist/common/billing/credit-conversion.js +48 -0
  46. package/dist/common/billing/credit-conversion.js.map +1 -0
  47. package/dist/common/billing/grant-credits.d.ts +28 -0
  48. package/dist/common/billing/grant-credits.js +201 -0
  49. package/dist/common/billing/grant-credits.js.map +1 -0
  50. package/dist/common/billing/plans.d.ts +13 -0
  51. package/dist/common/billing/plans.js +44 -0
  52. package/dist/common/billing/plans.js.map +1 -0
  53. package/dist/common/billing/rollover-logic.d.ts +13 -0
  54. package/dist/common/billing/rollover-logic.js +174 -0
  55. package/dist/common/billing/rollover-logic.js.map +1 -0
  56. package/dist/common/billing/stripe-api.d.ts +31 -0
  57. package/dist/common/billing/stripe-api.js +104 -0
  58. package/dist/common/billing/stripe-api.js.map +1 -0
  59. package/dist/common/browser-actions.d.ts +62 -62
  60. package/dist/common/constants/grant-priorities.d.ts +2 -0
  61. package/dist/common/constants/grant-priorities.js +10 -0
  62. package/dist/common/constants/grant-priorities.js.map +1 -0
  63. package/dist/common/constants.d.ts +28 -1
  64. package/dist/common/constants.js +36 -7
  65. package/dist/common/constants.js.map +1 -1
  66. package/dist/common/db/schema.d.ts +323 -86
  67. package/dist/common/db/schema.js +55 -13
  68. package/dist/common/db/schema.js.map +1 -1
  69. package/dist/common/env.mjs +4 -1
  70. package/dist/common/env.mjs.map +1 -1
  71. package/dist/common/message-image-handling.d.ts +41 -0
  72. package/dist/common/message-image-handling.js +57 -0
  73. package/dist/common/message-image-handling.js.map +1 -0
  74. package/dist/common/types/agent-state.d.ts +8 -8
  75. package/dist/common/types/grant.d.ts +2 -0
  76. package/dist/common/types/grant.js +10 -0
  77. package/dist/common/types/grant.js.map +1 -0
  78. package/dist/common/types/usage.d.ts +37 -18
  79. package/dist/common/types/usage.js +9 -6
  80. package/dist/common/types/usage.js.map +1 -1
  81. package/dist/common/util/__tests__/saxy.test.d.ts +1 -0
  82. package/dist/common/util/__tests__/saxy.test.js +262 -0
  83. package/dist/common/util/__tests__/saxy.test.js.map +1 -0
  84. package/dist/common/util/credentials.d.ts +2 -2
  85. package/dist/common/util/dates.d.ts +10 -1
  86. package/dist/common/util/dates.js +11 -2
  87. package/dist/common/util/dates.js.map +1 -1
  88. package/dist/common/util/logger.js +32 -14
  89. package/dist/common/util/logger.js.map +1 -1
  90. package/dist/common/util/process-stream.d.ts +8 -0
  91. package/dist/common/util/process-stream.js +102 -0
  92. package/dist/common/util/process-stream.js.map +1 -0
  93. package/dist/common/util/promise.d.ts +1 -1
  94. package/dist/common/util/promise.js +2 -2
  95. package/dist/common/util/promise.js.map +1 -1
  96. package/dist/common/util/referral-credits.d.ts +1 -0
  97. package/dist/common/util/referral-credits.js +48 -0
  98. package/dist/common/util/referral-credits.js.map +1 -0
  99. package/dist/common/util/saxy.d.ts +8 -0
  100. package/dist/common/util/saxy.js +35 -12
  101. package/dist/common/util/saxy.js.map +1 -1
  102. package/dist/common/util/sync-failure.d.ts +1 -0
  103. package/dist/common/util/sync-failure.js +57 -0
  104. package/dist/common/util/sync-failure.js.map +1 -0
  105. package/dist/common/websockets/websocket-schema.d.ts +530 -480
  106. package/dist/index.js +1 -1
  107. package/dist/utils/__tests__/xml-stream-parser.test.js +6 -10
  108. package/dist/utils/__tests__/xml-stream-parser.test.js.map +1 -1
  109. package/package.json +1 -1
  110. package/dist/common/logger.d.ts +0 -1
  111. package/dist/common/logger.js +0 -7
  112. package/dist/common/logger.js.map +0 -1
  113. package/dist/common/util/constants.d.ts +0 -1
  114. package/dist/common/util/constants.js +0 -7
  115. package/dist/common/util/constants.js.map +0 -1
  116. package/dist/common/util/helpers.d.ts +0 -1
  117. package/dist/common/util/helpers.js +0 -6
  118. package/dist/common/util/helpers.js.map +0 -1
  119. package/dist/common/util/token-counter.d.ts +0 -3
  120. package/dist/common/util/token-counter.js +0 -27
  121. package/dist/common/util/token-counter.js.map +0 -1
@@ -0,0 +1,201 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.getPreviousFreeGrantAmount = getPreviousFreeGrantAmount;
30
+ exports.calculateTotalReferralBonus = calculateTotalReferralBonus;
31
+ exports.processAndGrantCredit = processAndGrantCredit;
32
+ exports.revokeGrantByOperationId = revokeGrantByOperationId;
33
+ const db_1 = __importDefault(require("../db"));
34
+ const schema = __importStar(require("../db/schema"));
35
+ const logger_1 = require("../util/logger");
36
+ const grant_priorities_1 = require("../constants/grant-priorities");
37
+ const drizzle_orm_1 = require("drizzle-orm");
38
+ const constants_1 = require("../constants");
39
+ const promise_1 = require("../util/promise");
40
+ const sync_failure_1 = require("../util/sync-failure");
41
+ /**
42
+ * Finds the amount of the most recent expired 'free' grant for a user.
43
+ * If no expired 'free' grant is found, returns the default free limit.
44
+ * @param userId The ID of the user.
45
+ * @returns The amount of the last expired free grant or the default.
46
+ */
47
+ async function getPreviousFreeGrantAmount(userId) {
48
+ const now = new Date();
49
+ const lastExpiredFreeGrant = await db_1.default
50
+ .select({
51
+ principal: schema.creditLedger.principal,
52
+ })
53
+ .from(schema.creditLedger)
54
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.creditLedger.user_id, userId), (0, drizzle_orm_1.eq)(schema.creditLedger.type, 'free'), (0, drizzle_orm_1.lte)(schema.creditLedger.expires_at, now) // Grant has expired
55
+ ))
56
+ .orderBy((0, drizzle_orm_1.desc)(schema.creditLedger.expires_at)) // Most recent expiry first
57
+ .limit(1);
58
+ if (lastExpiredFreeGrant.length > 0) {
59
+ logger_1.logger.debug({ userId, amount: lastExpiredFreeGrant[0].principal }, 'Found previous expired free grant amount.');
60
+ return lastExpiredFreeGrant[0].principal;
61
+ }
62
+ else {
63
+ logger_1.logger.debug({ userId, defaultAmount: constants_1.CREDITS_USAGE_LIMITS.FREE }, 'No previous expired free grant found. Using default.');
64
+ return constants_1.CREDITS_USAGE_LIMITS.FREE; // Default if no previous grant found
65
+ }
66
+ }
67
+ /**
68
+ * Calculates the total referral bonus credits a user should receive based on
69
+ * their referral history (both as referrer and referred).
70
+ * @param userId The ID of the user.
71
+ * @returns The total referral bonus credits earned.
72
+ */
73
+ async function calculateTotalReferralBonus(userId) {
74
+ try {
75
+ const result = await db_1.default
76
+ .select({
77
+ totalCredits: (0, drizzle_orm_1.sql) `COALESCE(SUM(${schema.referral.credits}), 0)`,
78
+ })
79
+ .from(schema.referral)
80
+ .where((0, drizzle_orm_1.or)((0, drizzle_orm_1.eq)(schema.referral.referrer_id, userId), (0, drizzle_orm_1.eq)(schema.referral.referred_id, userId)));
81
+ const totalBonus = parseInt(result[0]?.totalCredits ?? '0');
82
+ logger_1.logger.debug({ userId, totalBonus }, 'Calculated total referral bonus.');
83
+ return totalBonus;
84
+ }
85
+ catch (error) {
86
+ logger_1.logger.error({ userId, error }, 'Error calculating total referral bonus. Returning 0.');
87
+ return 0;
88
+ }
89
+ }
90
+ /**
91
+ * Core grant operation wrapped in a single DB transaction.
92
+ */
93
+ async function grantCreditOperation(userId, amount, type, description, expiresAt, operationId) {
94
+ await db_1.default.transaction(async (tx) => {
95
+ const now = new Date();
96
+ // First check for any negative balances
97
+ const negativeGrants = await tx
98
+ .select()
99
+ .from(schema.creditLedger)
100
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.creditLedger.user_id, userId), (0, drizzle_orm_1.or)((0, drizzle_orm_1.isNull)(schema.creditLedger.expires_at), (0, drizzle_orm_1.gt)(schema.creditLedger.expires_at, now))))
101
+ .then((grants) => grants.filter((g) => g.balance < 0));
102
+ if (negativeGrants.length > 0) {
103
+ const totalDebt = negativeGrants.reduce((sum, g) => sum + Math.abs(g.balance), 0);
104
+ for (const grant of negativeGrants) {
105
+ await tx
106
+ .update(schema.creditLedger)
107
+ .set({ balance: 0 })
108
+ .where((0, drizzle_orm_1.eq)(schema.creditLedger.operation_id, grant.operation_id));
109
+ }
110
+ const remainingAmount = Math.max(0, amount - totalDebt);
111
+ if (remainingAmount > 0) {
112
+ await tx.insert(schema.creditLedger).values({
113
+ operation_id: operationId,
114
+ user_id: userId,
115
+ principal: amount,
116
+ balance: remainingAmount,
117
+ type,
118
+ description: totalDebt > 0
119
+ ? `${description} (${totalDebt} credits used to clear existing debt)`
120
+ : description,
121
+ priority: grant_priorities_1.GRANT_PRIORITIES[type],
122
+ expires_at: expiresAt,
123
+ created_at: now,
124
+ });
125
+ }
126
+ }
127
+ else {
128
+ // No debt - create grant normally
129
+ await tx.insert(schema.creditLedger).values({
130
+ operation_id: operationId,
131
+ user_id: userId,
132
+ principal: amount,
133
+ balance: amount,
134
+ type,
135
+ description,
136
+ priority: grant_priorities_1.GRANT_PRIORITIES[type],
137
+ expires_at: expiresAt,
138
+ created_at: now,
139
+ });
140
+ }
141
+ logger_1.logger.info({ userId, operationId, type, amount, expiresAt }, 'Created new credit grant');
142
+ });
143
+ }
144
+ /**
145
+ * Processes a credit grant request with retries and failure logging.
146
+ */
147
+ async function processAndGrantCredit(userId, amount, type, description, expiresAt, operationId) {
148
+ try {
149
+ await (0, promise_1.withRetry)(() => grantCreditOperation(userId, amount, type, description, expiresAt, operationId), {
150
+ maxRetries: 3,
151
+ retryIf: () => true,
152
+ onRetry: (error, attempt) => {
153
+ logger_1.logger.warn({ operationId, attempt, error }, `processAndGrantCredit retry ${attempt}`);
154
+ },
155
+ });
156
+ }
157
+ catch (error) {
158
+ await (0, sync_failure_1.logSyncFailure)(operationId, error.message, 'internal');
159
+ logger_1.logger.error({ operationId, error }, 'processAndGrantCredit failed after retries, logged to sync_failure');
160
+ throw error;
161
+ }
162
+ }
163
+ /**
164
+ * Revokes credits from a specific grant by operation ID.
165
+ * This sets the balance to 0 and updates the description to indicate a refund.
166
+ *
167
+ * @param operationId The operation ID of the grant to revoke
168
+ * @param reason The reason for revoking the credits (e.g. refund)
169
+ * @returns true if the grant was found and revoked, false otherwise
170
+ */
171
+ async function revokeGrantByOperationId(operationId, reason) {
172
+ return await db_1.default.transaction(async (tx) => {
173
+ const grant = await tx.query.creditLedger.findFirst({
174
+ where: (0, drizzle_orm_1.eq)(schema.creditLedger.operation_id, operationId),
175
+ });
176
+ if (!grant) {
177
+ logger_1.logger.warn({ operationId }, 'Attempted to revoke non-existent grant');
178
+ return false;
179
+ }
180
+ if (grant.balance < 0) {
181
+ logger_1.logger.warn({ operationId, currentBalance: grant.balance }, 'Cannot revoke grant with negative balance - user has already spent these credits');
182
+ return false;
183
+ }
184
+ await tx
185
+ .update(schema.creditLedger)
186
+ .set({
187
+ principal: 0,
188
+ balance: 0,
189
+ description: `${grant.description} (Revoked: ${reason})`,
190
+ })
191
+ .where((0, drizzle_orm_1.eq)(schema.creditLedger.operation_id, operationId));
192
+ logger_1.logger.info({
193
+ operationId,
194
+ userId: grant.user_id,
195
+ revokedAmount: grant.balance,
196
+ reason,
197
+ }, 'Revoked credit grant');
198
+ return true;
199
+ });
200
+ }
201
+ //# sourceMappingURL=grant-credits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grant-credits.js","sourceRoot":"","sources":["../../src/billing/grant-credits.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { UsageLimits } from '../constants';
2
+ /**
3
+ * Determines the user's plan based on their Stripe Price ID.
4
+ * Defaults to FREE if no matching price ID is found.
5
+ * NOTE: Requires backend environment variables for Stripe Price IDs.
6
+ */
7
+ export declare function getPlanFromPriceId(priceId: string | null | undefined, proPriceId?: string, moarProPriceId?: string): UsageLimits;
8
+ /**
9
+ * Gets the total monthly credit grant amount for a given plan and user.
10
+ * This includes both their plan's base credits and any referral bonuses,
11
+ * combined into a single number.
12
+ */
13
+ export declare function getMonthlyGrantForPlan(plan: UsageLimits, userId?: string): Promise<number>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPlanFromPriceId = getPlanFromPriceId;
4
+ exports.getMonthlyGrantForPlan = getMonthlyGrantForPlan;
5
+ const constants_1 = require("../constants");
6
+ const grant_credits_1 = require("./grant-credits");
7
+ const logger_1 = require("../util/logger");
8
+ /**
9
+ * Determines the user's plan based on their Stripe Price ID.
10
+ * Defaults to FREE if no matching price ID is found.
11
+ * NOTE: Requires backend environment variables for Stripe Price IDs.
12
+ */
13
+ function getPlanFromPriceId(priceId, proPriceId, moarProPriceId) {
14
+ if (!priceId) {
15
+ return constants_1.UsageLimits.FREE;
16
+ }
17
+ if (proPriceId && priceId === proPriceId) {
18
+ return constants_1.UsageLimits.PRO;
19
+ }
20
+ if (moarProPriceId && priceId === moarProPriceId) {
21
+ return constants_1.UsageLimits.MOAR_PRO;
22
+ }
23
+ return constants_1.UsageLimits.FREE; // Default to FREE for unknown or non-matching IDs
24
+ }
25
+ /**
26
+ * Gets the total monthly credit grant amount for a given plan and user.
27
+ * This includes both their plan's base credits and any referral bonuses,
28
+ * combined into a single number.
29
+ */
30
+ async function getMonthlyGrantForPlan(plan, userId) {
31
+ // If no userId provided, just return the plan's base limit
32
+ if (!userId) {
33
+ return constants_1.PLAN_CONFIGS[plan]?.limit ?? constants_1.PLAN_CONFIGS[constants_1.UsageLimits.FREE].limit;
34
+ }
35
+ // Calculate total grant by adding base plan amount and referral bonuses
36
+ const [freeGrantAmount, referralBonus] = await Promise.all([
37
+ (0, grant_credits_1.getPreviousFreeGrantAmount)(userId),
38
+ (0, grant_credits_1.calculateTotalReferralBonus)(userId),
39
+ ]);
40
+ const totalGrant = freeGrantAmount + referralBonus;
41
+ logger_1.logger.debug({ userId, plan, freeGrantAmount, referralBonus, totalGrant }, 'Calculated total monthly grant amount');
42
+ return totalGrant;
43
+ }
44
+ //# sourceMappingURL=plans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plans.js","sourceRoot":"","sources":["../../src/billing/plans.ts"],"names":[],"mappings":";;AASA,gDAiBC;AAOD,wDAqBC;AAtDD,4CAAwD;AACxD,mDAAyF;AACzF,2CAAuC;AAEvC;;;;GAIG;AACH,SAAgB,kBAAkB,CAChC,OAAkC,EAClC,UAAmB,EACnB,cAAuB;IAEvB,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,uBAAW,CAAC,IAAI,CAAA;IACzB,CAAC;IAED,IAAI,UAAU,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QACzC,OAAO,uBAAW,CAAC,GAAG,CAAA;IACxB,CAAC;IACD,IAAI,cAAc,IAAI,OAAO,KAAK,cAAc,EAAE,CAAC;QACjD,OAAO,uBAAW,CAAC,QAAQ,CAAA;IAC7B,CAAC;IAED,OAAO,uBAAW,CAAC,IAAI,CAAA,CAAC,kDAAkD;AAC5E,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,sBAAsB,CAC1C,IAAiB,EACjB,MAAe;IAEf,2DAA2D;IAC3D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,wBAAY,CAAC,IAAI,CAAC,EAAE,KAAK,IAAI,wBAAY,CAAC,uBAAW,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;IAC1E,CAAC;IAED,wEAAwE;IACxE,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzD,IAAA,0CAA0B,EAAC,MAAM,CAAC;QAClC,IAAA,2CAA2B,EAAC,MAAM,CAAC;KACpC,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,eAAe,GAAG,aAAa,CAAA;IAClD,eAAM,CAAC,KAAK,CACV,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,EAC5D,uCAAuC,CACxC,CAAA;IACD,OAAO,UAAU,CAAA;AACnB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Calculates the rollover amount for a user at the end of their billing cycle
3
+ * and applies the necessary database updates (creates rollover grant, resets usage, updates reset date).
4
+ *
5
+ * NOTE: This function performs database writes and should be called within a transaction
6
+ * if invoked alongside other database operations. It also depends on the `credit_grants`
7
+ * table and the modified `user` table schema being in place.
8
+ *
9
+ * @param userId The ID of the user whose cycle is ending.
10
+ * @param cycleEndDate The exact date and time the billing cycle ended.
11
+ * @returns A Promise resolving when the process is complete.
12
+ */
13
+ export declare function calculateAndApplyRollover(userId: string, cycleEndDate: Date): Promise<void>;
@@ -0,0 +1,174 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.calculateAndApplyRollover = calculateAndApplyRollover;
30
+ const db_1 = __importDefault(require("../db"));
31
+ const schema = __importStar(require("../db/schema"));
32
+ const drizzle_orm_1 = require("drizzle-orm");
33
+ const balance_calculator_1 = require("./balance-calculator");
34
+ const logger_1 = require("../util/logger");
35
+ const dates_1 = require("../util/dates"); // Assuming this utility exists/is created
36
+ // Define which grant types contribute to rollover
37
+ const ROLLOVER_GRANT_TYPES = ['purchase', 'rollover'];
38
+ /**
39
+ * Calculates the rollover amount for a user at the end of their billing cycle
40
+ * and applies the necessary database updates (creates rollover grant, resets usage, updates reset date).
41
+ *
42
+ * NOTE: This function performs database writes and should be called within a transaction
43
+ * if invoked alongside other database operations. It also depends on the `credit_grants`
44
+ * table and the modified `user` table schema being in place.
45
+ *
46
+ * @param userId The ID of the user whose cycle is ending.
47
+ * @param cycleEndDate The exact date and time the billing cycle ended.
48
+ * @returns A Promise resolving when the process is complete.
49
+ */
50
+ async function calculateAndApplyRollover(userId, cycleEndDate) {
51
+ logger_1.logger.info({ userId, cycleEndDate }, 'Starting end-of-cycle rollover process');
52
+ try {
53
+ // 1. Fetch the user's usage for the cycle that just ended
54
+ // We need the user record to get the usage *before* resetting it.
55
+ const user = await db_1.default.query.user.findFirst({
56
+ where: (0, drizzle_orm_1.eq)(schema.user.id, userId),
57
+ columns: { usage: true, next_quota_reset: true }, // Also get current reset date
58
+ });
59
+ if (!user) {
60
+ logger_1.logger.error({ userId }, 'User not found during rollover calculation.');
61
+ return;
62
+ }
63
+ // Ensure we don't run rollover multiple times for the same cycle
64
+ if (user.next_quota_reset && user.next_quota_reset > cycleEndDate) {
65
+ logger_1.logger.warn({ userId, cycleEndDate, nextReset: user.next_quota_reset }, 'Rollover attempted for a cycle that has not yet ended or already processed. Skipping.');
66
+ return;
67
+ }
68
+ const endedCycleUsage = user.usage;
69
+ // Calculate the start date of the ended cycle (approximate)
70
+ const cycleStartDate = new Date(cycleEndDate);
71
+ cycleStartDate.setMonth(cycleStartDate.getMonth() - 1); // Approximation
72
+ // 2. Fetch all grants that were active *during* the ended cycle
73
+ // Active during cycle: created_at < cycleEndDate AND (expires_at IS NULL OR expires_at > cycleStartDate)
74
+ const grantsActiveDuringCycle = await db_1.default
75
+ .select()
76
+ .from(schema.creditGrants)
77
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(schema.creditGrants.user_id, userId), (0, drizzle_orm_1.lte)(schema.creditGrants.created_at, cycleEndDate), // Created before or exactly at cycle end
78
+ (0, drizzle_orm_1.or)((0, drizzle_orm_1.isNull)(schema.creditGrants.expires_at), // Never expires
79
+ (0, drizzle_orm_1.gt)(schema.creditGrants.expires_at, cycleStartDate) // Expired after cycle start
80
+ )))
81
+ // 3. Order grants by priority ASC, then created_at ASC
82
+ .orderBy((0, drizzle_orm_1.asc)(schema.creditGrants.priority), (0, drizzle_orm_1.asc)(schema.creditGrants.created_at));
83
+ // 4. Initialize rollover amount
84
+ let rolloverAmount = 0;
85
+ // 5. Initialize usage to account for from the ended cycle
86
+ let usageToAccountFor = endedCycleUsage;
87
+ logger_1.logger.debug({ userId, endedCycleUsage, grantsCount: grantsActiveDuringCycle.length }, 'Simulating consumption for ended cycle');
88
+ // 6. Simulate Cycle Consumption
89
+ for (const grant of grantsActiveDuringCycle) {
90
+ const consumedFromThisGrant = Math.min(grant.amount, usageToAccountFor);
91
+ const remainingInThisGrant = grant.amount - consumedFromThisGrant;
92
+ usageToAccountFor -= consumedFromThisGrant;
93
+ logger_1.logger.trace({
94
+ userId,
95
+ grantId: grant.operation_id,
96
+ grantType: grant.type,
97
+ grantAmount: grant.amount,
98
+ consumed: consumedFromThisGrant,
99
+ remainingInGrant: remainingInThisGrant,
100
+ usageLeftToAccount: usageToAccountFor,
101
+ }, 'Processing grant during rollover simulation');
102
+ // 7. Check for Rollover Contribution
103
+ if (remainingInThisGrant > 0 &&
104
+ ROLLOVER_GRANT_TYPES.includes(grant.type)) {
105
+ rolloverAmount += remainingInThisGrant;
106
+ logger_1.logger.trace({
107
+ userId,
108
+ grantId: grant.operation_id,
109
+ addedToRollover: remainingInThisGrant,
110
+ newTotalRollover: rolloverAmount,
111
+ }, 'Grant contributed to rollover amount');
112
+ }
113
+ if (usageToAccountFor <= 0) {
114
+ // If usage is covered, check remaining grants for rollover contributions
115
+ const remainingGrants = grantsActiveDuringCycle.slice(grantsActiveDuringCycle.indexOf(grant) + 1);
116
+ for (const remainingGrant of remainingGrants) {
117
+ if (ROLLOVER_GRANT_TYPES.includes(remainingGrant.type)) {
118
+ rolloverAmount += remainingGrant.amount;
119
+ logger_1.logger.trace({
120
+ userId,
121
+ grantId: remainingGrant.operation_id,
122
+ addedToRollover: remainingGrant.amount,
123
+ newTotalRollover: rolloverAmount,
124
+ }, 'Untouched grant contributed to rollover amount');
125
+ }
126
+ }
127
+ break; // All usage accounted for
128
+ }
129
+ }
130
+ logger_1.logger.info({ userId, endedCycleUsage, calculatedRollover: rolloverAmount }, 'Rollover calculation complete');
131
+ // 8. Database Updates (Perform as a transaction)
132
+ await db_1.default.transaction(async (tx) => {
133
+ // Insert new 'rollover' grant if amount > 0
134
+ if (rolloverAmount > 0) {
135
+ const rolloverGrantId = `rollover-${userId}-${cycleEndDate.toISOString()}`;
136
+ await tx
137
+ .insert(schema.creditGrants)
138
+ .values({
139
+ operation_id: rolloverGrantId,
140
+ user_id: userId,
141
+ amount: rolloverAmount,
142
+ amount_remaining: rolloverAmount, // Initialize amount_remaining to the full amount
143
+ type: 'rollover', // Use string literal directly
144
+ priority: balance_calculator_1.GRANT_PRIORITIES.rollover, // Use defined priority
145
+ expires_at: null, // Rollover credits don't expire
146
+ description: `Rollover from cycle ending ${cycleEndDate.toLocaleDateString()}`,
147
+ // stripe_grant_id is NULL for local grants
148
+ })
149
+ .onConflictDoNothing(); // Avoid duplicate rollovers if run concurrently
150
+ logger_1.logger.debug({ userId, rolloverAmount }, 'Inserted rollover grant');
151
+ }
152
+ else {
153
+ logger_1.logger.debug({ userId }, 'No rollover amount to grant.');
154
+ }
155
+ // Update the user: reset usage and set next reset date
156
+ const nextResetDate = (0, dates_1.getNextQuotaReset)(cycleEndDate); // Calculate the next reset date based on the cycle end
157
+ await tx
158
+ .update(schema.user)
159
+ .set({
160
+ usage: 0,
161
+ next_quota_reset: nextResetDate,
162
+ })
163
+ .where((0, drizzle_orm_1.eq)(schema.user.id, userId));
164
+ logger_1.logger.info({ userId, nextResetDate: nextResetDate.toISOString() }, 'User usage reset and next reset date updated');
165
+ });
166
+ logger_1.logger.info({ userId }, 'Rollover process completed successfully.');
167
+ }
168
+ catch (error) {
169
+ logger_1.logger.error({ userId, cycleEndDate, error }, 'Error during rollover process');
170
+ // Depending on trigger mechanism, might need error handling/retry logic here
171
+ throw error; // Re-throw to indicate failure if called from a job
172
+ }
173
+ }
174
+ //# sourceMappingURL=rollover-logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rollover-logic.js","sourceRoot":"","sources":["../../src/billing/rollover-logic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { CreditType } from './balance-calculator';
2
+ import Stripe from 'stripe';
3
+ export declare const stripeApi: {
4
+ credits: {
5
+ /**
6
+ * Creates a customer balance transaction in Stripe to grant credits.
7
+ * NOTE: Database insertion for a local 'creditGrant' record is commented out
8
+ * as the 'creditGrant' table doesn't exist in the current schema.
9
+ */
10
+ create({ userId, amount, type, expiresAt, description, metadata }: {
11
+ userId: string;
12
+ amount: number;
13
+ type: CreditType;
14
+ expiresAt?: Date;
15
+ description?: string;
16
+ metadata?: Record<string, string | number | null>;
17
+ }): Promise<Stripe.CustomerBalanceTransaction>;
18
+ /**
19
+ * Retrieves the customer's cash balance from Stripe.
20
+ * Note: This is the overall cash balance, typically negative if credits have been granted.
21
+ * It does NOT represent the sum of specific "credit grants".
22
+ */
23
+ getBalance(userId: string): Promise<number>;
24
+ };
25
+ usage: {
26
+ /**
27
+ * Reports usage to a Stripe meter.
28
+ */
29
+ report(userId: string, amount: number, metadata?: Record<string, any>): Promise<Stripe.Response<Stripe.Billing.MeterEvent>>;
30
+ };
31
+ };
@@ -0,0 +1,104 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.stripeApi = void 0;
30
+ const db_1 = __importDefault(require("../db"));
31
+ const schema = __importStar(require("../db/schema"));
32
+ const drizzle_orm_1 = require("drizzle-orm");
33
+ const stripe_1 = require("../util/stripe");
34
+ const balance_calculator_1 = require("./balance-calculator");
35
+ // Helper to get Stripe Customer ID
36
+ async function getCustomerId(userId) {
37
+ const user = await db_1.default.query.user.findFirst({
38
+ where: (0, drizzle_orm_1.eq)(schema.user.id, userId),
39
+ columns: { stripe_customer_id: true }
40
+ });
41
+ if (!user?.stripe_customer_id) {
42
+ throw new Error(`Stripe customer ID not found for user ${userId}`);
43
+ }
44
+ return user.stripe_customer_id;
45
+ }
46
+ exports.stripeApi = {
47
+ credits: {
48
+ /**
49
+ * Creates a customer balance transaction in Stripe to grant credits.
50
+ * NOTE: Database insertion for a local 'creditGrant' record is commented out
51
+ * as the 'creditGrant' table doesn't exist in the current schema.
52
+ */
53
+ async create({ userId, amount, type, expiresAt, description, metadata = {} }) {
54
+ const customerId = await getCustomerId(userId);
55
+ const priority = balance_calculator_1.CREDIT_PRIORITIES[type];
56
+ const amountInCents = -Math.abs(amount);
57
+ const transaction = await stripe_1.stripeServer.customers.createBalanceTransaction(customerId, {
58
+ amount: amountInCents,
59
+ currency: 'usd',
60
+ description: description ?? `${type} credits granted`,
61
+ metadata: {
62
+ ...metadata,
63
+ type: type,
64
+ priority: priority.toString(),
65
+ }
66
+ });
67
+ return transaction;
68
+ },
69
+ /**
70
+ * Retrieves the customer's cash balance from Stripe.
71
+ * Note: This is the overall cash balance, typically negative if credits have been granted.
72
+ * It does NOT represent the sum of specific "credit grants".
73
+ */
74
+ async getBalance(userId) {
75
+ const customerId = await getCustomerId(userId);
76
+ const customer = await stripe_1.stripeServer.customers.retrieve(customerId);
77
+ if (customer.deleted) {
78
+ return 0;
79
+ }
80
+ return customer.balance ?? 0;
81
+ }
82
+ },
83
+ usage: {
84
+ /**
85
+ * Reports usage to a Stripe meter.
86
+ */
87
+ async report(userId, amount, metadata = {}) {
88
+ const customerId = await getCustomerId(userId);
89
+ const eventName = 'codebuff_credits_used';
90
+ return stripe_1.stripeServer.billing.meterEvents.create({
91
+ event_name: eventName,
92
+ timestamp: Math.floor(Date.now() / 1000),
93
+ payload: {
94
+ stripe_customer_id: customerId,
95
+ value: amount.toString(),
96
+ ...metadata
97
+ }
98
+ }).catch((err) => {
99
+ throw err;
100
+ });
101
+ }
102
+ }
103
+ };
104
+ //# sourceMappingURL=stripe-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe-api.js","sourceRoot":"","sources":["../../src/billing/stripe-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAsB;AACtB,qDAAsC;AACtC,6CAAgC;AAChC,2CAA6C;AAC7C,6DAAoE;AAGpE,mCAAmC;AACnC,KAAK,UAAU,aAAa,CAAC,MAAc;IACvC,MAAM,IAAI,GAAG,MAAM,YAAE,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;QACvC,KAAK,EAAE,IAAA,gBAAE,EAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC;QACjC,OAAO,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE;KACxC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,EAAE,kBAAkB,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,yCAAyC,MAAM,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,CAAC,kBAAkB,CAAC;AACnC,CAAC;AAEY,QAAA,SAAS,GAAG;IACvB,OAAO,EAAE;QACP;;;;WAIG;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,GAAG,EAAE,EAOzE;YACC,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9C,MAAM,QAAQ,GAAG,sCAAiB,CAAC,IAAI,CAAC,CAAA;YAExC,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAExC,MAAM,WAAW,GAAG,MAAM,qBAAY,CAAC,SAAS,CAAC,wBAAwB,CACrE,UAAU,EACV;gBACI,MAAM,EAAE,aAAa;gBACrB,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,WAAW,IAAI,GAAG,IAAI,kBAAkB;gBACrD,QAAQ,EAAE;oBACN,GAAG,QAAQ;oBACX,IAAI,EAAE,IAAI;oBACV,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE;iBAChC;aACJ,CACJ,CAAC;YAEF,OAAO,WAAW,CAAA;QACpB,CAAC;QAED;;;;WAIG;QACH,KAAK,CAAC,UAAU,CAAC,MAAc;YAC3B,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,MAAM,qBAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YAEnE,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,CAAC,CAAC;YACb,CAAC;YAED,OAAO,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;QACjC,CAAC;KACF;IACD,KAAK,EAAE;QACL;;WAEG;QACH,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,MAAc,EAAE,WAAgC,EAAE;YAC7E,MAAM,UAAU,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAA;YAC9C,MAAM,SAAS,GAAG,uBAAuB,CAAC;YAE1C,OAAO,qBAAY,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC;gBAC7C,UAAU,EAAE,SAAS;gBACrB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;gBACxC,OAAO,EAAE;oBACP,kBAAkB,EAAE,UAAU;oBAC9B,KAAK,EAAE,MAAM,CAAC,QAAQ,EAAE;oBACxB,GAAG,QAAQ;iBACZ;aACF,CAAC,CAAC,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE;gBACpB,MAAM,GAAG,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC;KACF;CACF,CAAA"}