guardrail-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/autopilot.test.d.ts +7 -0
- package/dist/__tests__/autopilot.test.d.ts.map +1 -0
- package/dist/__tests__/autopilot.test.js +156 -0
- package/dist/__tests__/tier-config.test.d.ts +9 -0
- package/dist/__tests__/tier-config.test.d.ts.map +1 -0
- package/dist/__tests__/tier-config.test.js +230 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
- package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash-inline.test.js +62 -0
- package/dist/__tests__/utils/hash.test.d.ts +3 -0
- package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
- package/dist/__tests__/utils/hash.test.js +95 -0
- package/dist/__tests__/utils/simple.test.d.ts +1 -0
- package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/simple.test.js +10 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
- package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils-simple.test.js +6 -0
- package/dist/__tests__/utils/utils.test.d.ts +15 -0
- package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils/utils.test.js +172 -0
- package/dist/autopilot/autopilot-runner.d.ts +33 -0
- package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
- package/dist/autopilot/autopilot-runner.js +479 -0
- package/dist/autopilot/index.d.ts +6 -0
- package/dist/autopilot/index.d.ts.map +1 -0
- package/dist/autopilot/index.js +25 -0
- package/dist/autopilot/types.d.ts +102 -0
- package/dist/autopilot/types.d.ts.map +1 -0
- package/dist/autopilot/types.js +18 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +22 -0
- package/dist/cache/redis-cache.d.ts +145 -0
- package/dist/cache/redis-cache.d.ts.map +1 -0
- package/dist/cache/redis-cache.js +459 -0
- package/dist/ci/github-actions.d.ts +77 -0
- package/dist/ci/github-actions.d.ts.map +1 -0
- package/dist/ci/github-actions.js +277 -0
- package/dist/ci/index.d.ts +12 -0
- package/dist/ci/index.d.ts.map +1 -0
- package/dist/ci/index.js +27 -0
- package/dist/ci/pre-commit.d.ts +65 -0
- package/dist/ci/pre-commit.d.ts.map +1 -0
- package/dist/ci/pre-commit.js +286 -0
- package/dist/entitlements.d.ts +149 -0
- package/dist/entitlements.d.ts.map +1 -0
- package/dist/entitlements.js +464 -0
- package/dist/env.d.ts +113 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +204 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
- package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
- package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
- package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
- package/dist/fix-packs/generate-fix-packs.js +505 -0
- package/dist/fix-packs/index.d.ts +8 -0
- package/dist/fix-packs/index.d.ts.map +1 -0
- package/dist/fix-packs/index.js +23 -0
- package/dist/fix-packs/types.d.ts +113 -0
- package/dist/fix-packs/types.d.ts.map +1 -0
- package/dist/fix-packs/types.js +71 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/metrics/prometheus.d.ts +99 -0
- package/dist/metrics/prometheus.d.ts.map +1 -0
- package/dist/metrics/prometheus.js +306 -0
- package/dist/quota-ledger.d.ts +119 -0
- package/dist/quota-ledger.d.ts.map +1 -0
- package/dist/quota-ledger.js +462 -0
- package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
- package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
- package/dist/rbac/__tests__/permissions.test.js +350 -0
- package/dist/rbac/index.d.ts +9 -0
- package/dist/rbac/index.d.ts.map +1 -0
- package/dist/rbac/index.js +32 -0
- package/dist/rbac/permissions.d.ts +71 -0
- package/dist/rbac/permissions.d.ts.map +1 -0
- package/dist/rbac/permissions.js +247 -0
- package/dist/rbac/types.d.ts +69 -0
- package/dist/rbac/types.d.ts.map +1 -0
- package/dist/rbac/types.js +213 -0
- package/dist/tier-config.d.ts +203 -0
- package/dist/tier-config.d.ts.map +1 -0
- package/dist/tier-config.js +675 -0
- package/dist/types.d.ts +365 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +127 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
- package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
- package/dist/verified-autofix/format-validator.d.ts +101 -0
- package/dist/verified-autofix/format-validator.d.ts.map +1 -0
- package/dist/verified-autofix/format-validator.js +446 -0
- package/dist/verified-autofix/index.d.ts +14 -0
- package/dist/verified-autofix/index.d.ts.map +1 -0
- package/dist/verified-autofix/index.js +39 -0
- package/dist/verified-autofix/pipeline.d.ts +68 -0
- package/dist/verified-autofix/pipeline.d.ts.map +1 -0
- package/dist/verified-autofix/pipeline.js +330 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
- package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
- package/dist/verified-autofix/repo-fingerprint.js +396 -0
- package/dist/verified-autofix/workspace.d.ts +83 -0
- package/dist/verified-autofix/workspace.d.ts.map +1 -0
- package/dist/verified-autofix/workspace.js +454 -0
- package/dist/verified-autofix.d.ts +182 -0
- package/dist/verified-autofix.d.ts.map +1 -0
- package/dist/verified-autofix.js +1021 -0
- package/dist/visualization/dependency-graph.d.ts +79 -0
- package/dist/visualization/dependency-graph.d.ts.map +1 -0
- package/dist/visualization/dependency-graph.js +399 -0
- package/dist/visualization/index.d.ts +5 -0
- package/dist/visualization/index.d.ts.map +1 -0
- package/dist/visualization/index.js +20 -0
- package/package.json +29 -0
- package/src/__tests__/autopilot.test.ts +196 -0
- package/src/__tests__/tier-config.test.ts +289 -0
- package/src/__tests__/utils/hash-inline.test.ts +76 -0
- package/src/__tests__/utils/hash.test.ts +119 -0
- package/src/__tests__/utils/simple.test.ts +10 -0
- package/src/__tests__/utils/utils-simple.test.ts +5 -0
- package/src/__tests__/utils/utils.test.ts +203 -0
- package/src/autopilot/autopilot-runner.ts +503 -0
- package/src/autopilot/index.ts +6 -0
- package/src/autopilot/types.ts +119 -0
- package/src/cache/index.ts +7 -0
- package/src/cache/redis-cache.d.ts +155 -0
- package/src/cache/redis-cache.d.ts.map +1 -0
- package/src/cache/redis-cache.ts +517 -0
- package/src/ci/github-actions.ts +335 -0
- package/src/ci/index.ts +12 -0
- package/src/ci/pre-commit.ts +338 -0
- package/src/db/usage-schema.prisma +114 -0
- package/src/entitlements.ts +570 -0
- package/src/env.d.ts +68 -0
- package/src/env.d.ts.map +1 -0
- package/src/env.ts +247 -0
- package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
- package/src/fix-packs/generate-fix-packs.ts +577 -0
- package/src/fix-packs/index.ts +8 -0
- package/src/fix-packs/types.ts +206 -0
- package/src/index.d.ts +7 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.ts +12 -0
- package/src/metrics/prometheus.d.ts +104 -0
- package/src/metrics/prometheus.d.ts.map +1 -0
- package/src/metrics/prometheus.ts +446 -0
- package/src/quota-ledger.ts +548 -0
- package/src/rbac/__tests__/permissions.test.ts +446 -0
- package/src/rbac/index.ts +46 -0
- package/src/rbac/permissions.ts +301 -0
- package/src/rbac/types.ts +298 -0
- package/src/tier-config.json +157 -0
- package/src/tier-config.ts +815 -0
- package/src/types.d.ts +365 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +441 -0
- package/src/utils.d.ts +36 -0
- package/src/utils.d.ts.map +1 -0
- package/src/utils.ts +140 -0
- package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
- package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
- package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
- package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
- package/src/verified-autofix/format-validator.ts +517 -0
- package/src/verified-autofix/index.ts +63 -0
- package/src/verified-autofix/pipeline.ts +403 -0
- package/src/verified-autofix/repo-fingerprint.ts +459 -0
- package/src/verified-autofix/workspace.ts +531 -0
- package/src/verified-autofix.ts +1187 -0
- package/src/visualization/dependency-graph.d.ts +85 -0
- package/src/visualization/dependency-graph.d.ts.map +1 -0
- package/src/visualization/dependency-graph.ts +495 -0
- package/src/visualization/index.ts +5 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entitlements System - SINGLE SOURCE OF TRUTH
|
|
3
|
+
*
|
|
4
|
+
* This module is the canonical entitlements implementation for Guardrail.
|
|
5
|
+
* It handles feature access, usage limits, tier enforcement, and seat management.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This TypeScript file is compiled to dist/entitlements.js
|
|
8
|
+
* DO NOT create separate entitlements.js files elsewhere in the codebase.
|
|
9
|
+
* All consumers (API, CLI, etc.) should import from @guardrail/core.
|
|
10
|
+
*/
|
|
11
|
+
import { Feature, SEAT_PRICING, SeatPricing, TIER_CONFIG, Tier, TierConfig, calculateEffectiveSeats, canAddMember, formatSeatInfo, getMinimumTierForFeature, getTierConfig, isValidTier, validateSeatReduction } from './tier-config';
|
|
12
|
+
export type { Feature, SeatPricing, Tier, TierConfig };
|
|
13
|
+
export { SEAT_PRICING, TIER_CONFIG, calculateEffectiveSeats, canAddMember, formatSeatInfo, getMinimumTierForFeature, getTierConfig, isValidTier, validateSeatReduction };
|
|
14
|
+
export interface UsageRecord {
|
|
15
|
+
tier: Tier;
|
|
16
|
+
userId?: string;
|
|
17
|
+
email?: string;
|
|
18
|
+
periodStart: string;
|
|
19
|
+
periodEnd: string;
|
|
20
|
+
usage: {
|
|
21
|
+
scans: number;
|
|
22
|
+
realityRuns: number;
|
|
23
|
+
aiAgentRuns: number;
|
|
24
|
+
gateRuns: number;
|
|
25
|
+
fixRuns: number;
|
|
26
|
+
};
|
|
27
|
+
lastUpdated: string;
|
|
28
|
+
lastServerSync?: string;
|
|
29
|
+
pendingSync?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface EntitlementCheck {
|
|
32
|
+
allowed: boolean;
|
|
33
|
+
reason?: string;
|
|
34
|
+
usage?: number;
|
|
35
|
+
limit?: number;
|
|
36
|
+
upgradePrompt?: string;
|
|
37
|
+
source?: 'server' | 'cache' | 'local' | 'offline';
|
|
38
|
+
}
|
|
39
|
+
export interface SeatCheck {
|
|
40
|
+
allowed: boolean;
|
|
41
|
+
reason?: string;
|
|
42
|
+
effectiveSeats: number;
|
|
43
|
+
baseSeats: number;
|
|
44
|
+
purchasedSeats: number;
|
|
45
|
+
currentMembers: number;
|
|
46
|
+
}
|
|
47
|
+
export interface OrganizationSeats {
|
|
48
|
+
tier: Tier;
|
|
49
|
+
baseSeats: number;
|
|
50
|
+
purchasedExtraSeats: number;
|
|
51
|
+
effectiveSeats: number;
|
|
52
|
+
currentMembers: number;
|
|
53
|
+
seatPricing: SeatPricing;
|
|
54
|
+
}
|
|
55
|
+
export declare class EntitlementsManager {
|
|
56
|
+
private configDir;
|
|
57
|
+
private usageFile;
|
|
58
|
+
private licenseFile;
|
|
59
|
+
constructor();
|
|
60
|
+
/**
|
|
61
|
+
* Get current tier from license file or environment
|
|
62
|
+
*/
|
|
63
|
+
getCurrentTier(): Promise<Tier>;
|
|
64
|
+
/**
|
|
65
|
+
* Validate API key against server and return tier
|
|
66
|
+
*
|
|
67
|
+
* SECURITY: Tier is determined server-side only.
|
|
68
|
+
* The API key string contains NO tier information.
|
|
69
|
+
*/
|
|
70
|
+
private validateApiKeyWithServer;
|
|
71
|
+
/**
|
|
72
|
+
* Check if a feature is available for the current tier
|
|
73
|
+
*/
|
|
74
|
+
checkFeature(feature: Feature): Promise<EntitlementCheck>;
|
|
75
|
+
/**
|
|
76
|
+
* Check usage limits
|
|
77
|
+
*/
|
|
78
|
+
checkLimit(limitType: 'scans' | 'realityRuns' | 'aiAgentRuns'): Promise<EntitlementCheck>;
|
|
79
|
+
/**
|
|
80
|
+
* Track usage
|
|
81
|
+
*/
|
|
82
|
+
trackUsage(type: 'scans' | 'realityRuns' | 'aiAgentRuns' | 'gateRuns' | 'fixRuns', count?: number): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Enforce feature access (throws if not allowed)
|
|
85
|
+
*/
|
|
86
|
+
enforceFeature(feature: Feature): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Enforce usage limits (throws if exceeded)
|
|
89
|
+
*/
|
|
90
|
+
enforceLimit(limitType: 'scans' | 'realityRuns' | 'aiAgentRuns'): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Check if a member can be added to an organization
|
|
93
|
+
*/
|
|
94
|
+
checkSeatLimit(tier: Tier, currentMemberCount: number, purchasedExtraSeats?: number): SeatCheck;
|
|
95
|
+
/**
|
|
96
|
+
* Get organization seat information
|
|
97
|
+
*/
|
|
98
|
+
getOrganizationSeats(tier: Tier, purchasedExtraSeats: number, currentMembers: number): OrganizationSeats;
|
|
99
|
+
/**
|
|
100
|
+
* Validate seat reduction before processing
|
|
101
|
+
*/
|
|
102
|
+
validateSeatReduction(currentMemberCount: number, currentPurchasedSeats: number, newPurchasedSeats: number, tier: Tier): {
|
|
103
|
+
safe: boolean;
|
|
104
|
+
requiresAction: boolean;
|
|
105
|
+
excessMembers: number;
|
|
106
|
+
message: string;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Get usage for current billing period
|
|
110
|
+
*/
|
|
111
|
+
getUsage(): Promise<UsageRecord>;
|
|
112
|
+
/**
|
|
113
|
+
* Get tier configuration
|
|
114
|
+
*/
|
|
115
|
+
getTierConfig(tier: Tier): TierConfig;
|
|
116
|
+
/**
|
|
117
|
+
* Get all tier configurations
|
|
118
|
+
*/
|
|
119
|
+
getAllTiers(): Record<Tier, TierConfig>;
|
|
120
|
+
/**
|
|
121
|
+
* Get usage summary for display
|
|
122
|
+
*/
|
|
123
|
+
getUsageSummary(): Promise<string>;
|
|
124
|
+
/**
|
|
125
|
+
* Format upgrade prompt for CLI output
|
|
126
|
+
*/
|
|
127
|
+
formatUpgradePrompt(currentTier: Tier, requiredTier: Tier | null, feature: Feature): string;
|
|
128
|
+
/**
|
|
129
|
+
* Format limit exceeded prompt
|
|
130
|
+
*/
|
|
131
|
+
formatLimitUpgradePrompt(currentTier: Tier, limitType: string, current: number, limit: number): string;
|
|
132
|
+
private isNewBillingPeriod;
|
|
133
|
+
private createNewUsageRecord;
|
|
134
|
+
private ensureConfigDir;
|
|
135
|
+
private saveUsage;
|
|
136
|
+
private readLicense;
|
|
137
|
+
private progressBar;
|
|
138
|
+
}
|
|
139
|
+
export declare const entitlements: EntitlementsManager;
|
|
140
|
+
export declare const checkFeature: (feature: Feature) => Promise<EntitlementCheck>;
|
|
141
|
+
export declare const checkLimit: (limitType: "scans" | "realityRuns" | "aiAgentRuns") => Promise<EntitlementCheck>;
|
|
142
|
+
export declare const enforceFeature: (feature: Feature) => Promise<void>;
|
|
143
|
+
export declare const enforceLimit: (limitType: "scans" | "realityRuns" | "aiAgentRuns") => Promise<void>;
|
|
144
|
+
export declare const trackUsage: (type: "scans" | "realityRuns" | "aiAgentRuns" | "gateRuns" | "fixRuns", count?: number) => Promise<void>;
|
|
145
|
+
export declare const getCurrentTier: () => Promise<"free" | "starter" | "pro" | "compliance" | "enterprise" | "unlimited">;
|
|
146
|
+
export declare const getUsageSummary: () => Promise<string>;
|
|
147
|
+
export declare const checkSeatLimit: (tier: Tier, currentMemberCount: number, purchasedExtraSeats?: number) => SeatCheck;
|
|
148
|
+
export declare const getOrganizationSeats: (tier: Tier, purchasedExtraSeats: number, currentMembers: number) => OrganizationSeats;
|
|
149
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../src/entitlements.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Entitlements System - SINGLE SOURCE OF TRUTH
|
|
4
|
+
*
|
|
5
|
+
* This module is the canonical entitlements implementation for Guardrail.
|
|
6
|
+
* It handles feature access, usage limits, tier enforcement, and seat management.
|
|
7
|
+
*
|
|
8
|
+
* IMPORTANT: This TypeScript file is compiled to dist/entitlements.js
|
|
9
|
+
* DO NOT create separate entitlements.js files elsewhere in the codebase.
|
|
10
|
+
* All consumers (API, CLI, etc.) should import from @guardrail/core.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.getOrganizationSeats = exports.checkSeatLimit = exports.getUsageSummary = exports.getCurrentTier = exports.trackUsage = exports.enforceLimit = exports.enforceFeature = exports.checkLimit = exports.checkFeature = exports.entitlements = exports.EntitlementsManager = exports.validateSeatReduction = exports.isValidTier = exports.getTierConfig = exports.getMinimumTierForFeature = exports.formatSeatInfo = exports.canAddMember = exports.calculateEffectiveSeats = exports.TIER_CONFIG = exports.SEAT_PRICING = void 0;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const tier_config_1 = require("./tier-config");
|
|
51
|
+
Object.defineProperty(exports, "SEAT_PRICING", { enumerable: true, get: function () { return tier_config_1.SEAT_PRICING; } });
|
|
52
|
+
Object.defineProperty(exports, "TIER_CONFIG", { enumerable: true, get: function () { return tier_config_1.TIER_CONFIG; } });
|
|
53
|
+
Object.defineProperty(exports, "calculateEffectiveSeats", { enumerable: true, get: function () { return tier_config_1.calculateEffectiveSeats; } });
|
|
54
|
+
Object.defineProperty(exports, "canAddMember", { enumerable: true, get: function () { return tier_config_1.canAddMember; } });
|
|
55
|
+
Object.defineProperty(exports, "formatSeatInfo", { enumerable: true, get: function () { return tier_config_1.formatSeatInfo; } });
|
|
56
|
+
Object.defineProperty(exports, "getMinimumTierForFeature", { enumerable: true, get: function () { return tier_config_1.getMinimumTierForFeature; } });
|
|
57
|
+
Object.defineProperty(exports, "getTierConfig", { enumerable: true, get: function () { return tier_config_1.getTierConfig; } });
|
|
58
|
+
Object.defineProperty(exports, "isValidTier", { enumerable: true, get: function () { return tier_config_1.isValidTier; } });
|
|
59
|
+
Object.defineProperty(exports, "validateSeatReduction", { enumerable: true, get: function () { return tier_config_1.validateSeatReduction; } });
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// ENTITLEMENTS MANAGER
|
|
62
|
+
// ============================================================================
|
|
63
|
+
class EntitlementsManager {
|
|
64
|
+
configDir;
|
|
65
|
+
usageFile;
|
|
66
|
+
licenseFile;
|
|
67
|
+
constructor() {
|
|
68
|
+
this.configDir = path.join(os.homedir(), '.guardrail');
|
|
69
|
+
this.usageFile = path.join(this.configDir, 'usage.json');
|
|
70
|
+
this.licenseFile = path.join(this.configDir, 'license.json');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get current tier from license file or environment
|
|
74
|
+
*/
|
|
75
|
+
async getCurrentTier() {
|
|
76
|
+
// Skip entitlements check if explicitly disabled
|
|
77
|
+
if (process.env['GUARDRAIL_SKIP_ENTITLEMENTS'] === '1') {
|
|
78
|
+
return 'unlimited';
|
|
79
|
+
}
|
|
80
|
+
// Check environment override (for CI/testing)
|
|
81
|
+
if (process.env['GUARDRAIL_TIER']) {
|
|
82
|
+
return process.env['GUARDRAIL_TIER'];
|
|
83
|
+
}
|
|
84
|
+
// Check for license file
|
|
85
|
+
try {
|
|
86
|
+
const license = await this.readLicense();
|
|
87
|
+
if (license?.tier && (0, tier_config_1.isValidTier)(license.tier)) {
|
|
88
|
+
// Check expiration
|
|
89
|
+
if (license.expiresAt && new Date(license.expiresAt) < new Date()) {
|
|
90
|
+
return 'free';
|
|
91
|
+
}
|
|
92
|
+
return license.tier;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// No license file
|
|
97
|
+
}
|
|
98
|
+
// Check for API key - validate against server (NO local tier parsing)
|
|
99
|
+
const apiKey = process.env['GUARDRAIL_API_KEY'];
|
|
100
|
+
if (apiKey) {
|
|
101
|
+
const tier = await this.validateApiKeyWithServer(apiKey);
|
|
102
|
+
if (tier)
|
|
103
|
+
return tier;
|
|
104
|
+
}
|
|
105
|
+
return 'free';
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Validate API key against server and return tier
|
|
109
|
+
*
|
|
110
|
+
* SECURITY: Tier is determined server-side only.
|
|
111
|
+
* The API key string contains NO tier information.
|
|
112
|
+
*/
|
|
113
|
+
async validateApiKeyWithServer(apiKey) {
|
|
114
|
+
const apiUrl = process.env['GUARDRAIL_API_URL'] || 'https://api.getguardrail.io';
|
|
115
|
+
try {
|
|
116
|
+
const response = await fetch(`${apiUrl}/api/api-keys/validate`, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: {
|
|
119
|
+
'Content-Type': 'application/json',
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify({ apiKey }),
|
|
122
|
+
});
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const result = await response.json();
|
|
127
|
+
if (result.valid && result.tier && (0, tier_config_1.isValidTier)(result.tier)) {
|
|
128
|
+
return result.tier;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Network error or server unavailable - fall back to free tier
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Check if a feature is available for the current tier
|
|
138
|
+
*/
|
|
139
|
+
async checkFeature(feature) {
|
|
140
|
+
const tier = await this.getCurrentTier();
|
|
141
|
+
const config = tier_config_1.TIER_CONFIG[tier];
|
|
142
|
+
// Unlimited tier has all features
|
|
143
|
+
if (tier === 'unlimited' || config.features.includes(feature)) {
|
|
144
|
+
return { allowed: true };
|
|
145
|
+
}
|
|
146
|
+
// Find the minimum tier that has this feature
|
|
147
|
+
const requiredTier = (0, tier_config_1.getMinimumTierForFeature)(feature);
|
|
148
|
+
return {
|
|
149
|
+
allowed: false,
|
|
150
|
+
reason: `'${feature}' requires ${requiredTier || 'higher'} tier`,
|
|
151
|
+
upgradePrompt: this.formatUpgradePrompt(tier, requiredTier, feature),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check usage limits
|
|
156
|
+
*/
|
|
157
|
+
async checkLimit(limitType) {
|
|
158
|
+
const tier = await this.getCurrentTier();
|
|
159
|
+
const config = tier_config_1.TIER_CONFIG[tier];
|
|
160
|
+
const usage = await this.getUsage();
|
|
161
|
+
const limitMap = {
|
|
162
|
+
scans: 'scansPerMonth',
|
|
163
|
+
realityRuns: 'realityRunsPerMonth',
|
|
164
|
+
aiAgentRuns: 'aiAgentRunsPerMonth',
|
|
165
|
+
};
|
|
166
|
+
const limitKey = limitMap[limitType];
|
|
167
|
+
const limit = config.limits[limitKey];
|
|
168
|
+
const current = usage.usage[limitType] || 0;
|
|
169
|
+
// Handle unlimited (-1)
|
|
170
|
+
if (limit === -1 || current < limit) {
|
|
171
|
+
return {
|
|
172
|
+
allowed: true,
|
|
173
|
+
usage: current,
|
|
174
|
+
limit: limit === -1 ? -1 : limit,
|
|
175
|
+
source: 'local',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
allowed: false,
|
|
180
|
+
reason: `Monthly ${limitType} limit reached (${current}/${limit})`,
|
|
181
|
+
usage: current,
|
|
182
|
+
limit,
|
|
183
|
+
upgradePrompt: this.formatLimitUpgradePrompt(tier, limitType, current, limit),
|
|
184
|
+
source: 'local',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Track usage
|
|
189
|
+
*/
|
|
190
|
+
async trackUsage(type, count = 1) {
|
|
191
|
+
const usage = await this.getUsage();
|
|
192
|
+
usage.usage[type] = (usage.usage[type] || 0) + count;
|
|
193
|
+
usage.lastUpdated = new Date().toISOString();
|
|
194
|
+
await this.saveUsage(usage);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Enforce feature access (throws if not allowed)
|
|
198
|
+
*/
|
|
199
|
+
async enforceFeature(feature) {
|
|
200
|
+
const check = await this.checkFeature(feature);
|
|
201
|
+
if (!check.allowed) {
|
|
202
|
+
const error = new Error(check.reason);
|
|
203
|
+
error.code = 'FEATURE_NOT_AVAILABLE';
|
|
204
|
+
error.upgradePrompt = check.upgradePrompt;
|
|
205
|
+
error.feature = feature;
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Enforce usage limits (throws if exceeded)
|
|
211
|
+
*/
|
|
212
|
+
async enforceLimit(limitType) {
|
|
213
|
+
const check = await this.checkLimit(limitType);
|
|
214
|
+
if (!check.allowed) {
|
|
215
|
+
const error = new Error(check.reason);
|
|
216
|
+
error.code = 'LIMIT_EXCEEDED';
|
|
217
|
+
error.upgradePrompt = check.upgradePrompt;
|
|
218
|
+
error.usage = check.usage;
|
|
219
|
+
error.limit = check.limit;
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// ============================================================================
|
|
224
|
+
// SEAT MANAGEMENT
|
|
225
|
+
// ============================================================================
|
|
226
|
+
/**
|
|
227
|
+
* Check if a member can be added to an organization
|
|
228
|
+
*/
|
|
229
|
+
checkSeatLimit(tier, currentMemberCount, purchasedExtraSeats = 0) {
|
|
230
|
+
const config = tier_config_1.TIER_CONFIG[tier];
|
|
231
|
+
const baseSeats = config.limits.teamMembers;
|
|
232
|
+
const result = (0, tier_config_1.canAddMember)(tier, currentMemberCount, purchasedExtraSeats);
|
|
233
|
+
return {
|
|
234
|
+
allowed: result.allowed,
|
|
235
|
+
reason: result.reason,
|
|
236
|
+
effectiveSeats: result.effectiveSeats === Infinity ? -1 : result.effectiveSeats,
|
|
237
|
+
baseSeats: baseSeats === -1 ? -1 : baseSeats,
|
|
238
|
+
purchasedSeats: purchasedExtraSeats,
|
|
239
|
+
currentMembers: currentMemberCount,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get organization seat information
|
|
244
|
+
*/
|
|
245
|
+
getOrganizationSeats(tier, purchasedExtraSeats, currentMembers) {
|
|
246
|
+
const config = tier_config_1.TIER_CONFIG[tier];
|
|
247
|
+
const baseSeats = config.limits.teamMembers;
|
|
248
|
+
const effectiveSeats = (0, tier_config_1.calculateEffectiveSeats)(tier, purchasedExtraSeats);
|
|
249
|
+
return {
|
|
250
|
+
tier,
|
|
251
|
+
baseSeats: baseSeats === -1 ? -1 : baseSeats,
|
|
252
|
+
purchasedExtraSeats,
|
|
253
|
+
effectiveSeats: effectiveSeats === Infinity ? -1 : effectiveSeats,
|
|
254
|
+
currentMembers,
|
|
255
|
+
seatPricing: tier_config_1.SEAT_PRICING[tier],
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Validate seat reduction before processing
|
|
260
|
+
*/
|
|
261
|
+
validateSeatReduction(currentMemberCount, currentPurchasedSeats, newPurchasedSeats, tier) {
|
|
262
|
+
const currentEffective = (0, tier_config_1.calculateEffectiveSeats)(tier, currentPurchasedSeats);
|
|
263
|
+
const newEffective = (0, tier_config_1.calculateEffectiveSeats)(tier, newPurchasedSeats);
|
|
264
|
+
return (0, tier_config_1.validateSeatReduction)(currentMemberCount, currentEffective === Infinity ? -1 : currentEffective, newEffective === Infinity ? -1 : newEffective);
|
|
265
|
+
}
|
|
266
|
+
// ============================================================================
|
|
267
|
+
// USAGE MANAGEMENT
|
|
268
|
+
// ============================================================================
|
|
269
|
+
/**
|
|
270
|
+
* Get usage for current billing period
|
|
271
|
+
*/
|
|
272
|
+
async getUsage() {
|
|
273
|
+
try {
|
|
274
|
+
await this.ensureConfigDir();
|
|
275
|
+
const content = await fs.promises.readFile(this.usageFile, 'utf8');
|
|
276
|
+
const usage = JSON.parse(content);
|
|
277
|
+
// Check if we need to reset for new period
|
|
278
|
+
if (this.isNewBillingPeriod(usage.periodStart)) {
|
|
279
|
+
return this.createNewUsageRecord();
|
|
280
|
+
}
|
|
281
|
+
return usage;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return this.createNewUsageRecord();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Get tier configuration
|
|
289
|
+
*/
|
|
290
|
+
getTierConfig(tier) {
|
|
291
|
+
return tier_config_1.TIER_CONFIG[tier];
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get all tier configurations
|
|
295
|
+
*/
|
|
296
|
+
getAllTiers() {
|
|
297
|
+
return tier_config_1.TIER_CONFIG;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get usage summary for display
|
|
301
|
+
*/
|
|
302
|
+
async getUsageSummary() {
|
|
303
|
+
const tier = await this.getCurrentTier();
|
|
304
|
+
const config = tier_config_1.TIER_CONFIG[tier];
|
|
305
|
+
const usage = await this.getUsage();
|
|
306
|
+
const formatLimit = (current, limit) => {
|
|
307
|
+
if (limit === -1)
|
|
308
|
+
return `${current} (unlimited)`;
|
|
309
|
+
const pct = Math.round((current / limit) * 100);
|
|
310
|
+
const bar = this.progressBar(pct);
|
|
311
|
+
return `${current}/${limit} ${bar} ${pct}%`;
|
|
312
|
+
};
|
|
313
|
+
let summary = '\n';
|
|
314
|
+
summary += `📊 Usage Summary (${config.name} tier)\n`;
|
|
315
|
+
summary += '─'.repeat(50) + '\n';
|
|
316
|
+
summary += `Scans: ${formatLimit(usage.usage.scans, config.limits.scansPerMonth)}\n`;
|
|
317
|
+
summary += `Reality Runs: ${formatLimit(usage.usage.realityRuns, config.limits.realityRunsPerMonth)}\n`;
|
|
318
|
+
summary += `AI Agent: ${formatLimit(usage.usage.aiAgentRuns, config.limits.aiAgentRunsPerMonth)}\n`;
|
|
319
|
+
summary += `Team Seats: ${(0, tier_config_1.formatSeatInfo)(tier)}\n`;
|
|
320
|
+
summary += '─'.repeat(50) + '\n';
|
|
321
|
+
summary += `Period: ${usage.periodStart.split('T')[0]} to ${usage.periodEnd.split('T')[0]}\n`;
|
|
322
|
+
return summary;
|
|
323
|
+
}
|
|
324
|
+
// ============================================================================
|
|
325
|
+
// UPGRADE PROMPTS
|
|
326
|
+
// ============================================================================
|
|
327
|
+
/**
|
|
328
|
+
* Format upgrade prompt for CLI output
|
|
329
|
+
*/
|
|
330
|
+
formatUpgradePrompt(currentTier, requiredTier, feature) {
|
|
331
|
+
const required = requiredTier ? tier_config_1.TIER_CONFIG[requiredTier] : null;
|
|
332
|
+
let prompt = '\n';
|
|
333
|
+
prompt += '╭─────────────────────────────────────────────────────────────╮\n';
|
|
334
|
+
prompt += '│ ⚡ UPGRADE REQUIRED │\n';
|
|
335
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
336
|
+
prompt += `│ Feature: ${feature.padEnd(48)}│\n`;
|
|
337
|
+
prompt += `│ Your tier: ${currentTier.padEnd(46)}│\n`;
|
|
338
|
+
if (required) {
|
|
339
|
+
prompt += `│ Required: ${requiredTier} ($${required.price}/month)`.padEnd(62) + '│\n';
|
|
340
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
341
|
+
prompt += `│ ${required.name} includes:`.padEnd(62) + '│\n';
|
|
342
|
+
// Show key features of required tier
|
|
343
|
+
const keyFeatures = required.features.slice(0, 5);
|
|
344
|
+
for (const f of keyFeatures) {
|
|
345
|
+
prompt += `│ ✓ ${f}`.padEnd(62) + '│\n';
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
349
|
+
prompt += '│ → guardrail upgrade │\n';
|
|
350
|
+
prompt += '│ → https://getguardrail.io/pricing │\n';
|
|
351
|
+
prompt += '╰─────────────────────────────────────────────────────────────╯\n';
|
|
352
|
+
return prompt;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Format limit exceeded prompt
|
|
356
|
+
*/
|
|
357
|
+
formatLimitUpgradePrompt(currentTier, limitType, current, limit) {
|
|
358
|
+
const config = tier_config_1.TIER_CONFIG[currentTier];
|
|
359
|
+
const nextConfig = tier_config_1.TIER_CONFIG[config.upsell.nextTier];
|
|
360
|
+
let prompt = '\n';
|
|
361
|
+
prompt += '╭─────────────────────────────────────────────────────────────╮\n';
|
|
362
|
+
prompt += '│ ⚠️ MONTHLY LIMIT REACHED │\n';
|
|
363
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
364
|
+
prompt += `│ ${limitType}: ${current}/${limit} used this month`.padEnd(62) + '│\n';
|
|
365
|
+
prompt += `│ Your tier: ${currentTier} ($${config.price}/month)`.padEnd(62) + '│\n';
|
|
366
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
367
|
+
prompt += `│ ${config.upsell.message}`.substring(0, 58).padEnd(62) + '│\n';
|
|
368
|
+
if (nextConfig && config.upsell.nextTier !== 'unlimited') {
|
|
369
|
+
const nextLimitMap = {
|
|
370
|
+
scans: 'scansPerMonth',
|
|
371
|
+
realityRuns: 'realityRunsPerMonth',
|
|
372
|
+
aiAgentRuns: 'aiAgentRunsPerMonth',
|
|
373
|
+
};
|
|
374
|
+
const nextLimit = nextConfig.limits[nextLimitMap[limitType] || 'scansPerMonth'];
|
|
375
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
376
|
+
prompt += `│ ${nextConfig.name} ($${nextConfig.price}/mo): ${nextLimit === -1 ? 'Unlimited' : nextLimit} ${limitType}/month`.padEnd(62) + '│\n';
|
|
377
|
+
}
|
|
378
|
+
prompt += '├─────────────────────────────────────────────────────────────┤\n';
|
|
379
|
+
prompt += '│ → guardrail upgrade │\n';
|
|
380
|
+
prompt += '│ → https://getguardrail.io/pricing │\n';
|
|
381
|
+
prompt += '╰─────────────────────────────────────────────────────────────╯\n';
|
|
382
|
+
return prompt;
|
|
383
|
+
}
|
|
384
|
+
// ============================================================================
|
|
385
|
+
// PRIVATE HELPERS
|
|
386
|
+
// ============================================================================
|
|
387
|
+
isNewBillingPeriod(periodStart) {
|
|
388
|
+
const start = new Date(periodStart);
|
|
389
|
+
const now = new Date();
|
|
390
|
+
// Monthly billing period
|
|
391
|
+
const nextPeriod = new Date(start);
|
|
392
|
+
nextPeriod.setMonth(nextPeriod.getMonth() + 1);
|
|
393
|
+
return now >= nextPeriod;
|
|
394
|
+
}
|
|
395
|
+
createNewUsageRecord() {
|
|
396
|
+
const now = new Date();
|
|
397
|
+
const periodEnd = new Date(now);
|
|
398
|
+
periodEnd.setMonth(periodEnd.getMonth() + 1);
|
|
399
|
+
return {
|
|
400
|
+
tier: 'free',
|
|
401
|
+
periodStart: now.toISOString(),
|
|
402
|
+
periodEnd: periodEnd.toISOString(),
|
|
403
|
+
usage: {
|
|
404
|
+
scans: 0,
|
|
405
|
+
realityRuns: 0,
|
|
406
|
+
aiAgentRuns: 0,
|
|
407
|
+
gateRuns: 0,
|
|
408
|
+
fixRuns: 0,
|
|
409
|
+
},
|
|
410
|
+
lastUpdated: now.toISOString(),
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
async ensureConfigDir() {
|
|
414
|
+
try {
|
|
415
|
+
await fs.promises.mkdir(this.configDir, { recursive: true });
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
// Directory exists
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
async saveUsage(usage) {
|
|
422
|
+
await this.ensureConfigDir();
|
|
423
|
+
await fs.promises.writeFile(this.usageFile, JSON.stringify(usage, null, 2));
|
|
424
|
+
}
|
|
425
|
+
async readLicense() {
|
|
426
|
+
try {
|
|
427
|
+
const content = await fs.promises.readFile(this.licenseFile, 'utf8');
|
|
428
|
+
return JSON.parse(content);
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
progressBar(percent) {
|
|
435
|
+
const filled = Math.min(10, Math.round(percent / 10));
|
|
436
|
+
const empty = 10 - filled;
|
|
437
|
+
const color = percent >= 90 ? '🔴' : percent >= 70 ? '🟡' : '🟢';
|
|
438
|
+
return `[${color.repeat(filled)}${'░'.repeat(empty)}]`;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
exports.EntitlementsManager = EntitlementsManager;
|
|
442
|
+
// ============================================================================
|
|
443
|
+
// SINGLETON EXPORT
|
|
444
|
+
// ============================================================================
|
|
445
|
+
exports.entitlements = new EntitlementsManager();
|
|
446
|
+
// Convenience exports
|
|
447
|
+
const checkFeature = (feature) => exports.entitlements.checkFeature(feature);
|
|
448
|
+
exports.checkFeature = checkFeature;
|
|
449
|
+
const checkLimit = (limitType) => exports.entitlements.checkLimit(limitType);
|
|
450
|
+
exports.checkLimit = checkLimit;
|
|
451
|
+
const enforceFeature = (feature) => exports.entitlements.enforceFeature(feature);
|
|
452
|
+
exports.enforceFeature = enforceFeature;
|
|
453
|
+
const enforceLimit = (limitType) => exports.entitlements.enforceLimit(limitType);
|
|
454
|
+
exports.enforceLimit = enforceLimit;
|
|
455
|
+
const trackUsage = (type, count) => exports.entitlements.trackUsage(type, count);
|
|
456
|
+
exports.trackUsage = trackUsage;
|
|
457
|
+
const getCurrentTier = () => exports.entitlements.getCurrentTier();
|
|
458
|
+
exports.getCurrentTier = getCurrentTier;
|
|
459
|
+
const getUsageSummary = () => exports.entitlements.getUsageSummary();
|
|
460
|
+
exports.getUsageSummary = getUsageSummary;
|
|
461
|
+
const checkSeatLimit = (tier, currentMemberCount, purchasedExtraSeats) => exports.entitlements.checkSeatLimit(tier, currentMemberCount, purchasedExtraSeats);
|
|
462
|
+
exports.checkSeatLimit = checkSeatLimit;
|
|
463
|
+
const getOrganizationSeats = (tier, purchasedExtraSeats, currentMembers) => exports.entitlements.getOrganizationSeats(tier, purchasedExtraSeats, currentMembers);
|
|
464
|
+
exports.getOrganizationSeats = getOrganizationSeats;
|