@cullit/core 2.3.1 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +11 -14
- package/dist/index.js +43 -41
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ interface PipelineResult {
|
|
|
71
71
|
duration: number;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
declare const VERSION = "2.
|
|
74
|
+
declare const VERSION = "2.4.1";
|
|
75
75
|
declare const DEFAULT_CATEGORIES: string[];
|
|
76
76
|
declare const DEFAULT_MODELS: Record<string, string>;
|
|
77
77
|
declare const AI_PROVIDERS: readonly ["anthropic", "openai", "gemini", "ollama", "none"];
|
|
@@ -85,9 +85,9 @@ declare const SOURCE_TYPES: readonly ["local", "jira", "linear", "gitlab", "bitb
|
|
|
85
85
|
declare const TIERS: readonly ["free", "pro", "team", "enterprise"];
|
|
86
86
|
declare const PAID_TIERS: readonly ["pro", "team", "enterprise"];
|
|
87
87
|
declare const TEAM_TIERS: readonly ["team", "enterprise"];
|
|
88
|
-
declare const
|
|
89
|
-
|
|
90
|
-
declare const
|
|
88
|
+
declare const TEAM_SEAT_PRICE = 8;
|
|
89
|
+
declare const TEAM_MIN_SEATS = 5;
|
|
90
|
+
declare const TEAM_ANNUAL_DISCOUNT = 0.15;
|
|
91
91
|
|
|
92
92
|
type LogLevel = 'quiet' | 'normal' | 'verbose';
|
|
93
93
|
interface Logger {
|
|
@@ -221,8 +221,7 @@ declare function analyzeReleaseReadiness(cwd?: string): ReleaseAdvisory;
|
|
|
221
221
|
*
|
|
222
222
|
* Free tier (no key): 3 AI gens/month, all providers (BYOK), publish to stdout/file only
|
|
223
223
|
* Pro tier (with key): 500 gens/month, all providers, all publishers, enrichments, audience/tone
|
|
224
|
-
* Team tier:
|
|
225
|
-
* Team 25: 5000 gens/month, 500 projects, branded widget, project templates, audit logs
|
|
224
|
+
* Team tier (5+ seats): all team features, dynamic limits based on seat count
|
|
226
225
|
* Enterprise tier: unlimited everything
|
|
227
226
|
*
|
|
228
227
|
* validateLicense() performs async remote validation with caching.
|
|
@@ -280,24 +279,22 @@ interface UsageLimits {
|
|
|
280
279
|
*/
|
|
281
280
|
declare function getTierLimits(tier: string): UsageLimits;
|
|
282
281
|
/**
|
|
283
|
-
* Get usage limits
|
|
284
|
-
*
|
|
282
|
+
* Get usage limits scaled by seat count for team plans.
|
|
283
|
+
* Seats scale limits: 100 gens/seat, 5 projects/seat (with tier base as minimum).
|
|
285
284
|
*/
|
|
286
|
-
declare function
|
|
285
|
+
declare function getTeamLimits(seats: number): UsageLimits;
|
|
287
286
|
type TeamFeature = 'drafts' | 'approvals' | 'shared_history' | 'project_templates' | 'hosted_changelog' | 'branded_widget' | 'team_publishers' | 'org_settings' | 'audit_logs' | 'team_analytics' | 'sso';
|
|
288
287
|
/**
|
|
289
288
|
* Check whether a license tier grants access to a Team/Enterprise feature.
|
|
290
289
|
*/
|
|
291
290
|
declare function isFeatureAllowed(feature: TeamFeature, tier: string, valid?: boolean): boolean;
|
|
292
291
|
/**
|
|
293
|
-
* Check whether a
|
|
294
|
-
*
|
|
295
|
-
* Falls back to tier-level check for features without plan restrictions.
|
|
292
|
+
* Check whether a plan/tier grants access to a feature.
|
|
293
|
+
* Enterprise gets everything. Team gets all team features.
|
|
296
294
|
*/
|
|
297
295
|
declare function isPlanFeatureAllowed(feature: TeamFeature, plan: string, tier: string, valid?: boolean): boolean;
|
|
298
296
|
/**
|
|
299
297
|
* Build a gating summary for a tier — which features are unlocked.
|
|
300
|
-
* When plan is provided, uses plan-aware feature checks.
|
|
301
298
|
*/
|
|
302
299
|
declare function getFeatureGating(tier: string, plan?: string): Record<TeamFeature, boolean>;
|
|
303
300
|
/**
|
|
@@ -412,4 +409,4 @@ declare function runPipeline(from: string, to: string, config: CullConfig, optio
|
|
|
412
409
|
templateProfile?: string;
|
|
413
410
|
}): Promise<PipelineResult>;
|
|
414
411
|
|
|
415
|
-
export { AI_PROVIDERS, AUDIENCES, CHANGE_CATEGORIES, type ChangeCategory, type ChangeEntry, type Collector, type CollectorFactory, CoreErrorCode, type CoreErrorCodeValue, CullitError, DEFAULT_CATEGORIES, DEFAULT_MODELS, ENRICHMENT_TYPES, type EnrichedContext, type EnrichedTicket, type Enricher, type EnricherFactory, FilePublisher, type Generator, type GeneratorFactory, GitCollector, type GitCommit, type GitDiff, type LicenseStatus, type LicenseTier, type LogLevel, type Logger, MultiRepoCollector, OUTPUT_FORMATS, PAID_TIERS, PUBLISHER_TYPES, type PipelineResult, type Publisher, type PublisherFactory, type RateLimitResult, type RateLimiter, type RateLimiterOptions, type ReleaseAdvisory, type ReleaseNotes, SOURCE_TYPES, type SemverBump, StdoutPublisher,
|
|
412
|
+
export { AI_PROVIDERS, AUDIENCES, CHANGE_CATEGORIES, type ChangeCategory, type ChangeEntry, type Collector, type CollectorFactory, CoreErrorCode, type CoreErrorCodeValue, CullitError, DEFAULT_CATEGORIES, DEFAULT_MODELS, ENRICHMENT_TYPES, type EnrichedContext, type EnrichedTicket, type Enricher, type EnricherFactory, FilePublisher, type Generator, type GeneratorFactory, GitCollector, type GitCommit, type GitDiff, type LicenseStatus, type LicenseTier, type LogLevel, type Logger, MultiRepoCollector, OUTPUT_FORMATS, PAID_TIERS, PUBLISHER_TYPES, type PipelineResult, type Publisher, type PublisherFactory, type RateLimitResult, type RateLimiter, type RateLimiterOptions, type ReleaseAdvisory, type ReleaseNotes, SOURCE_TYPES, type SemverBump, StdoutPublisher, TEAM_ANNUAL_DISCOUNT, TEAM_MIN_SEATS, TEAM_SEAT_PRICE, TEAM_TIERS, TIERS, TONES, type TeamFeature, TemplateGenerator, type UsageLimits, VERSION, analyzeReleaseReadiness, createLogger, createRateLimiter, escapeHtml, fetchWithTimeout, formatNotes, getCollector, getEnricher, getFeatureGating, getFormatter, getGenerator, getLatestTag, getPublisher, getRecentTags, getTeamLimits, getTierLimits, hasCollector, hasEnricher, hasGenerator, hasPublisher, isAudienceToneAllowed, isEnrichmentAllowed, isFeatureAllowed, isPlanFeatureAllowed, isProviderAllowed, isPublisherAllowed, listCollectors, listEnrichers, listFormatters, listGenerators, listPublishers, registerCollector, registerEnricher, registerFormatter, registerGenerator, registerPublisher, reportUsage, resolveLicense, runPipeline, upgradeMessage, validateLicense };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/constants.ts
|
|
2
|
-
var VERSION = "2.
|
|
2
|
+
var VERSION = "2.4.1";
|
|
3
3
|
var DEFAULT_CATEGORIES = ["features", "fixes", "breaking", "improvements", "chores"];
|
|
4
4
|
var DEFAULT_MODELS = {
|
|
5
5
|
anthropic: "claude-sonnet-4-20250514",
|
|
@@ -18,12 +18,9 @@ var SOURCE_TYPES = ["local", "jira", "linear", "gitlab", "bitbucket", "multi-rep
|
|
|
18
18
|
var TIERS = ["free", "pro", "team", "enterprise"];
|
|
19
19
|
var PAID_TIERS = ["pro", "team", "enterprise"];
|
|
20
20
|
var TEAM_TIERS = ["team", "enterprise"];
|
|
21
|
-
var
|
|
22
|
-
var
|
|
23
|
-
|
|
24
|
-
"team-10": 10,
|
|
25
|
-
"team-25": 25
|
|
26
|
-
};
|
|
21
|
+
var TEAM_SEAT_PRICE = 8;
|
|
22
|
+
var TEAM_MIN_SEATS = 5;
|
|
23
|
+
var TEAM_ANNUAL_DISCOUNT = 0.15;
|
|
27
24
|
|
|
28
25
|
// src/logger.ts
|
|
29
26
|
function createLogger(level = "normal") {
|
|
@@ -103,7 +100,7 @@ var GitCollector = class {
|
|
|
103
100
|
};
|
|
104
101
|
}
|
|
105
102
|
getLog(from, to) {
|
|
106
|
-
const format = "%H
|
|
103
|
+
const format = "%H%x1e%h%x1e%an%x1e%aI%x1e%s%x1e%b";
|
|
107
104
|
const separator = "---CULLIT_COMMIT---";
|
|
108
105
|
try {
|
|
109
106
|
return execFileSync(
|
|
@@ -126,9 +123,9 @@ var GitCollector = class {
|
|
|
126
123
|
const separator = "---CULLIT_COMMIT---";
|
|
127
124
|
const entries = log.split(separator).filter((e) => e.trim());
|
|
128
125
|
return entries.map((entry) => {
|
|
129
|
-
const parts = entry.trim().split("
|
|
126
|
+
const parts = entry.trim().split("");
|
|
130
127
|
const [hash, shortHash, author, date, message, ...bodyParts] = parts;
|
|
131
|
-
const body = bodyParts.join("
|
|
128
|
+
const body = bodyParts.join("").trim() || void 0;
|
|
132
129
|
const fullMessage = body ? `${message}
|
|
133
130
|
${body}` : message;
|
|
134
131
|
return {
|
|
@@ -207,7 +204,7 @@ function getLatestTag(cwd = process.cwd()) {
|
|
|
207
204
|
function getCommitsSince(from, to, cwd = process.cwd()) {
|
|
208
205
|
validateRef(from);
|
|
209
206
|
validateRef(to);
|
|
210
|
-
const format = "%H
|
|
207
|
+
const format = "%H%x1e%h%x1e%an%x1e%aI%x1e%s";
|
|
211
208
|
const separator = "---CULLIT_COMMIT---";
|
|
212
209
|
const log = execFileSync(
|
|
213
210
|
"git",
|
|
@@ -216,13 +213,13 @@ function getCommitsSince(from, to, cwd = process.cwd()) {
|
|
|
216
213
|
);
|
|
217
214
|
if (!log.trim()) return [];
|
|
218
215
|
return log.split(separator).filter((e) => e.trim()).map((entry) => {
|
|
219
|
-
const [hash, shortHash, author, date, ...msgParts] = entry.trim().split("
|
|
216
|
+
const [hash, shortHash, author, date, ...msgParts] = entry.trim().split("");
|
|
220
217
|
return {
|
|
221
218
|
hash: hash.trim(),
|
|
222
219
|
shortHash: shortHash.trim(),
|
|
223
220
|
author: author.trim(),
|
|
224
221
|
date: date.trim(),
|
|
225
|
-
message: msgParts.join("
|
|
222
|
+
message: msgParts.join("").trim()
|
|
226
223
|
};
|
|
227
224
|
});
|
|
228
225
|
}
|
|
@@ -762,6 +759,13 @@ var TEAM_ONLY_PUBLISHERS = /* @__PURE__ */ new Set(["confluence", "notion", "tea
|
|
|
762
759
|
var LICENSE_CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
763
760
|
var LICENSE_FAILURE_CACHE_TTL = 5 * 60 * 1e3;
|
|
764
761
|
var cachedValidation = null;
|
|
762
|
+
function isInternalHost(hostname) {
|
|
763
|
+
const h = hostname.toLowerCase();
|
|
764
|
+
if (h === "0.0.0.0" || h === "127.0.0.1" || h.startsWith("10.") || h.startsWith("192.168.") || h.startsWith("169.254.") || /^172\.(1[6-9]|2\d|3[01])\./.test(h)) return true;
|
|
765
|
+
if (h === "[::]" || h === "[::1]" || h.startsWith("[::ffff:") || h.startsWith("[fc") || h.startsWith("[fd") || h.startsWith("[fe80:") || h.startsWith("[fe80")) return true;
|
|
766
|
+
if (h === "localhost" || h.endsWith(".local") || h.endsWith(".internal")) return true;
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
765
769
|
function resolveLicense() {
|
|
766
770
|
const key = process.env.CULLIT_API_KEY?.trim();
|
|
767
771
|
if (!key) {
|
|
@@ -792,8 +796,7 @@ async function validateLicense() {
|
|
|
792
796
|
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && parsed.hostname === "localhost")) {
|
|
793
797
|
return { tier: "pro", valid: true, message: "CULLIT_LICENSE_URL must use https." };
|
|
794
798
|
}
|
|
795
|
-
|
|
796
|
-
if (h === "0.0.0.0" || h === "[::]" || h === "[::1]" || h === "127.0.0.1" || h.startsWith("10.") || h.startsWith("192.168.") || h.startsWith("169.254.") || /^172\.(1[6-9]|2\d|3[01])\./.test(h) || h.endsWith(".local") || h.endsWith(".internal")) {
|
|
799
|
+
if (isInternalHost(parsed.hostname)) {
|
|
797
800
|
return { tier: "pro", valid: true, message: "CULLIT_LICENSE_URL must not point to internal addresses." };
|
|
798
801
|
}
|
|
799
802
|
} catch {
|
|
@@ -858,39 +861,31 @@ var TIER_LIMITS = {
|
|
|
858
861
|
team: { generationsPerMonth: 2e3, maxProjects: 250 },
|
|
859
862
|
enterprise: { generationsPerMonth: Infinity, maxProjects: Infinity }
|
|
860
863
|
};
|
|
861
|
-
var PLAN_LIMITS = {
|
|
862
|
-
"team-10": { generationsPerMonth: 4e3, maxProjects: 350 },
|
|
863
|
-
"team-25": { generationsPerMonth: 5e3, maxProjects: 500 }
|
|
864
|
-
};
|
|
865
864
|
function getTierLimits(tier) {
|
|
866
865
|
return TIER_LIMITS[tier] || TIER_LIMITS.free;
|
|
867
866
|
}
|
|
868
|
-
function
|
|
869
|
-
|
|
867
|
+
function getTeamLimits(seats) {
|
|
868
|
+
const base = TIER_LIMITS.team;
|
|
869
|
+
if (seats <= TEAM_MIN_SEATS) return base;
|
|
870
|
+
return {
|
|
871
|
+
generationsPerMonth: Math.max(base.generationsPerMonth, seats * 100),
|
|
872
|
+
maxProjects: Math.max(base.maxProjects, seats * 5)
|
|
873
|
+
};
|
|
870
874
|
}
|
|
871
875
|
var FEATURE_TIERS = {
|
|
872
876
|
drafts: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
873
877
|
approvals: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
874
878
|
shared_history: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
875
|
-
project_templates: /* @__PURE__ */ new Set(["enterprise"]),
|
|
876
|
-
// plan-gated: team-25 via PLAN_FEATURES
|
|
879
|
+
project_templates: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
877
880
|
hosted_changelog: /* @__PURE__ */ new Set(["pro", "team", "enterprise"]),
|
|
878
|
-
branded_widget: /* @__PURE__ */ new Set(["enterprise"]),
|
|
879
|
-
// plan-gated: team-25 via PLAN_FEATURES
|
|
881
|
+
branded_widget: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
880
882
|
team_publishers: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
881
883
|
org_settings: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
882
|
-
audit_logs: /* @__PURE__ */ new Set(["enterprise"]),
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
// plan-gated: team-25 via PLAN_FEATURES
|
|
884
|
+
audit_logs: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
885
|
+
team_analytics: /* @__PURE__ */ new Set(["team", "enterprise"]),
|
|
886
|
+
// all team plans get analytics now
|
|
886
887
|
sso: /* @__PURE__ */ new Set(["enterprise"])
|
|
887
888
|
};
|
|
888
|
-
var PLAN_FEATURES = {
|
|
889
|
-
branded_widget: /* @__PURE__ */ new Set(["team-25"]),
|
|
890
|
-
project_templates: /* @__PURE__ */ new Set(["team-25"]),
|
|
891
|
-
audit_logs: /* @__PURE__ */ new Set(["team-25"]),
|
|
892
|
-
team_analytics: /* @__PURE__ */ new Set(["team-25"])
|
|
893
|
-
};
|
|
894
889
|
function isFeatureAllowed(feature, tier, valid = true) {
|
|
895
890
|
if (!valid) return false;
|
|
896
891
|
const allowed = FEATURE_TIERS[feature];
|
|
@@ -899,8 +894,6 @@ function isFeatureAllowed(feature, tier, valid = true) {
|
|
|
899
894
|
function isPlanFeatureAllowed(feature, plan, tier, valid = true) {
|
|
900
895
|
if (!valid) return false;
|
|
901
896
|
if (tier === "enterprise") return true;
|
|
902
|
-
const planSet = PLAN_FEATURES[feature];
|
|
903
|
-
if (planSet) return planSet.has(plan);
|
|
904
897
|
const tierSet = FEATURE_TIERS[feature];
|
|
905
898
|
return tierSet ? tierSet.has(tier) : false;
|
|
906
899
|
}
|
|
@@ -915,6 +908,12 @@ async function reportUsage(project = "default") {
|
|
|
915
908
|
const key = process.env.CULLIT_API_KEY?.trim();
|
|
916
909
|
const meterUrl = process.env.CULLIT_METER_URL?.trim();
|
|
917
910
|
if (!meterUrl || !key) return;
|
|
911
|
+
try {
|
|
912
|
+
const parsed = new URL(meterUrl);
|
|
913
|
+
if (isInternalHost(parsed.hostname)) return;
|
|
914
|
+
} catch {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
918
917
|
try {
|
|
919
918
|
await fetchWithTimeout(meterUrl, {
|
|
920
919
|
method: "POST",
|
|
@@ -1057,8 +1056,10 @@ var RedisRateLimiter = class {
|
|
|
1057
1056
|
body: JSON.stringify([
|
|
1058
1057
|
["ZREMRANGEBYSCORE", redisKey, "0", String(windowStart)],
|
|
1059
1058
|
["ZCARD", redisKey],
|
|
1059
|
+
["MULTI"],
|
|
1060
1060
|
["ZADD", redisKey, String(now), `${now}-${Math.random().toString(36).slice(2, 8)}`],
|
|
1061
|
-
["PEXPIRE", redisKey, String(this.windowMs)]
|
|
1061
|
+
["PEXPIRE", redisKey, String(this.windowMs)],
|
|
1062
|
+
["EXEC"]
|
|
1062
1063
|
]),
|
|
1063
1064
|
signal: AbortSignal.timeout(3e3)
|
|
1064
1065
|
});
|
|
@@ -1316,8 +1317,9 @@ export {
|
|
|
1316
1317
|
PUBLISHER_TYPES,
|
|
1317
1318
|
SOURCE_TYPES,
|
|
1318
1319
|
StdoutPublisher,
|
|
1319
|
-
|
|
1320
|
-
|
|
1320
|
+
TEAM_ANNUAL_DISCOUNT,
|
|
1321
|
+
TEAM_MIN_SEATS,
|
|
1322
|
+
TEAM_SEAT_PRICE,
|
|
1321
1323
|
TEAM_TIERS,
|
|
1322
1324
|
TIERS,
|
|
1323
1325
|
TONES,
|
|
@@ -1335,9 +1337,9 @@ export {
|
|
|
1335
1337
|
getFormatter,
|
|
1336
1338
|
getGenerator,
|
|
1337
1339
|
getLatestTag,
|
|
1338
|
-
getPlanLimits,
|
|
1339
1340
|
getPublisher,
|
|
1340
1341
|
getRecentTags,
|
|
1342
|
+
getTeamLimits,
|
|
1341
1343
|
getTierLimits,
|
|
1342
1344
|
hasCollector,
|
|
1343
1345
|
hasEnricher,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cullit/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core engine for Cullit — AI-powered release note generation.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@cullit/config": "2.
|
|
41
|
+
"@cullit/config": "2.4.1"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsup src/index.ts --format esm --dts --clean",
|