@taskforcehq/taskforce 0.3.307 → 0.3.308
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/components/views/PlanComparisonPage.d.ts +20 -15
- package/dist/components/views/PlanComparisonPage.js +214 -83
- package/dist/components/views/PlanComparisonPage.test.js +249 -14
- package/dist/components/views/PlansPage.js +15 -9
- package/dist/components/views/PlansPage.test.js +4 -2
- package/dist/core/GlobalSettingsService.js +18 -0
- package/dist/core/GlobalSettingsService.test.d.ts +1 -0
- package/dist/core/GlobalSettingsService.test.js +45 -0
- package/dist/core/PlanEntitlementService.d.ts +20 -0
- package/dist/core/PlanEntitlementService.js +215 -19
- package/dist/core/PlanFeatureCatalog.test.js +55 -0
- package/dist/core/Taskforce.d.ts +14 -0
- package/dist/core/Taskforce.js +6 -0
- package/dist/core/types.d.ts +3 -0
- package/dist/migrations/taskSchemaMigrations.js +37 -15
- package/dist/server/routes/admin.js +146 -5
- package/dist/server/routes/authSupport.d.ts +1 -0
- package/dist/server/routes/authSupport.js +2 -1
- package/dist/server/routes/billing.d.ts +1 -0
- package/dist/server/routes/billing.js +105 -63
- package/dist/server/routes/billing.test.js +159 -11
- package/dist/server/routes.test.js +91 -10
- package/dist/ui/agent-logos/ChatGPT.png +0 -0
- package/dist/ui/agent-logos/Gemini CLI.jpeg +0 -0
- package/dist/ui/agent-logos/antigravity.jpeg +0 -0
- package/dist/ui/agent-logos/claude-code.jpeg +0 -0
- package/dist/ui/agent-logos/codex.jpeg +0 -0
- package/dist/ui/agent-logos/cursor.png +0 -0
- package/dist/ui/agent-logos/openclaw.jpeg +0 -0
- package/dist/ui/agent-logos/windsurf.png +0 -0
- package/dist/ui/assets/{AgentsModule-2y82_3DO.js → AgentsModule-BS4Pi_nC.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BTLZZCiQ.js → AnnotatedAttachmentWorkspace-CCckzWYZ.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-D2epuNnG.js → ContextAttachmentManager-CfG_ER7C.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-sVa-3Do6.js → DocumentWorkspace-BVaWPA25.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-Zods27y_.js → EntityActivityTimeline-DSj0ThaK.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-D4XseTwW.js → InitiativesModule-AxMkWtxH.js} +1 -1
- package/dist/ui/assets/PlansPage-B_z-D6Nh.css +1 -0
- package/dist/ui/assets/PlansPage-Ndt7mYfo.js +1 -0
- package/dist/ui/assets/{TaskContextUpload-uC5qx4Bv.js → TaskContextUpload-Dw4rTF4i.js} +1 -1
- package/dist/ui/assets/{TaskSettings-CqvPAsTv.js → TaskSettings-f2ga42_V.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-oOA9bcdK.js → WorkflowsModule-E4UL3mov.js} +1 -1
- package/dist/ui/assets/documentReferences-BOUcJm6-.js +1 -0
- package/dist/ui/assets/{index-CYyodXsg.css → index-BvAbqx5Q.css} +1 -1
- package/dist/ui/assets/{index-607WPo_X.js → index-MAHKvFqp.js} +4 -4
- package/dist/ui/index.html +2 -2
- package/dist/ui/og-image.png +0 -0
- package/package.json +1 -1
- package/dist/ui/assets/PlansPage-D5AcbO0L.js +0 -1
- package/dist/ui/assets/PlansPage-Dvqsz5zc.css +0 -1
- package/dist/ui/assets/documentReferences-DPZuTgHh.js +0 -1
|
@@ -72,6 +72,7 @@ import { buildCloudMcpRuntimeAuth, resolveErrorStatusCode } from './shared.js';
|
|
|
72
72
|
import { buildAuthBehavior, buildSiteBehavior, getRouteEmailConfig, resolvePublicAuthBaseUrl, resolveRuntimeConfigBaseUrl, } from './authSupport.js';
|
|
73
73
|
import { applyWorkspaceRepair, scanWorkspaceRepair } from '../../sync/workspaceRepair.js';
|
|
74
74
|
import { testMintAppleClientSecret } from '../auth/providers/appleClientSecret.js';
|
|
75
|
+
const PRICING_PAGE_DESCRIPTION_MAX_LENGTH = 2000;
|
|
75
76
|
// === SECTION: Registration ===
|
|
76
77
|
export function registerAdminRoutes(deps) {
|
|
77
78
|
const { addRoute, core, context, requestWorkspaceId, requestUserId, requestRole, requestHeader, requestRuntimeMode, ensureAppAccess, ensureAdminRole, ensureCloudPlanData, requireCloudRuntime, resolveAuthenticatedUserId, resolveAdminRequestActor, auditAdminAction, resolveEffectiveObjectStorageConfig, getObjectStorageClient, taskAssetStore, normalizeDocumentPathInput, ensureWithinDir, getAssetMetadata, markAssetDeleted, clearAssetDeleted, removeAssetMetadata, getStorageTelemetry, appendStorageTelemetry, getStorageIntegrityHistory, appendStorageIntegrityHistory, getDocumentPurgeHistory, appendDocumentPurgeHistory, getCollaborationTelemetry, isCollaborationPolicyEnabled, normalizeMcpOauthScopeString, MCP_TOKEN_SCOPE_VALUES, MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS, MCP_WRITE_RATE_LIMIT_ENABLED, MCP_WRITE_RATE_LIMIT_ACTOR_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WORKSPACE_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WINDOW_MS, } = deps;
|
|
@@ -712,6 +713,88 @@ export function registerAdminRoutes(deps) {
|
|
|
712
713
|
res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to list feature catalog'), code: error?.code }));
|
|
713
714
|
}
|
|
714
715
|
});
|
|
716
|
+
addRoute('POST', '/api/platform/features', async (req, res) => {
|
|
717
|
+
if (!ensureCloudPlanData(req, res))
|
|
718
|
+
return;
|
|
719
|
+
if (!ensureAdminRole(req)) {
|
|
720
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
721
|
+
res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const body = await parseJsonBody(req);
|
|
725
|
+
const label = String(body?.label || '');
|
|
726
|
+
const description = body?.description === undefined || body?.description === null
|
|
727
|
+
? body?.description
|
|
728
|
+
: String(body.description);
|
|
729
|
+
const publicLabel = body?.publicLabel === undefined || body?.publicLabel === null
|
|
730
|
+
? body?.publicLabel
|
|
731
|
+
: String(body.publicLabel);
|
|
732
|
+
const publicDescription = body?.publicDescription === undefined || body?.publicDescription === null
|
|
733
|
+
? body?.publicDescription
|
|
734
|
+
: String(body.publicDescription);
|
|
735
|
+
const publicDescriptionVisible = body?.publicDescriptionVisible === undefined || body?.publicDescriptionVisible === null
|
|
736
|
+
? body?.publicDescriptionVisible
|
|
737
|
+
: (body.publicDescriptionVisible === false ? false : true);
|
|
738
|
+
const publicDisplayOrder = body?.publicDisplayOrder === undefined || body?.publicDisplayOrder === null || body?.publicDisplayOrder === ''
|
|
739
|
+
? null
|
|
740
|
+
: Math.max(0, Math.floor(Number(body.publicDisplayOrder)));
|
|
741
|
+
if (!label.trim()) {
|
|
742
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
743
|
+
res.end(JSON.stringify({ success: false, error: 'Feature label is required.', code: 'PLAN_FEATURE_LABEL_REQUIRED' }));
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (label.trim().length > 120) {
|
|
747
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
748
|
+
res.end(JSON.stringify({ success: false, error: 'Feature label must be 120 characters or fewer.', code: 'PLAN_FEATURE_LABEL_TOO_LONG' }));
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
if (typeof description === 'string' && description.trim().length > 280) {
|
|
752
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
753
|
+
res.end(JSON.stringify({ success: false, error: 'Feature description must be 280 characters or fewer.', code: 'PLAN_FEATURE_DESCRIPTION_TOO_LONG' }));
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
if (typeof publicLabel === 'string' && publicLabel.trim().length > 120) {
|
|
757
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
758
|
+
res.end(JSON.stringify({ success: false, error: 'Public label must be 120 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_LABEL_TOO_LONG' }));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (typeof publicDescription === 'string' && publicDescription.trim().length > 280) {
|
|
762
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
763
|
+
res.end(JSON.stringify({ success: false, error: 'Public description must be 280 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_DESCRIPTION_TOO_LONG' }));
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
if (publicDisplayOrder !== null && !Number.isFinite(publicDisplayOrder)) {
|
|
767
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
768
|
+
res.end(JSON.stringify({ success: false, error: 'Public display order must be a non-negative number.', code: 'PLAN_FEATURE_PUBLIC_DISPLAY_ORDER_INVALID' }));
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
try {
|
|
772
|
+
const feature = core.createCustomPlanFeatureCatalogEntry({
|
|
773
|
+
label,
|
|
774
|
+
description,
|
|
775
|
+
publicLabel,
|
|
776
|
+
publicDescription,
|
|
777
|
+
publicDescriptionVisible,
|
|
778
|
+
publicDisplayOrder
|
|
779
|
+
});
|
|
780
|
+
auditAdminAction(req, 'platform-plan-feature-catalog-created', {
|
|
781
|
+
featureKey: feature.featureKey,
|
|
782
|
+
label: feature.label,
|
|
783
|
+
description: feature.description,
|
|
784
|
+
publicLabel: feature.publicLabel ?? null,
|
|
785
|
+
publicDescription: feature.publicDescription ?? null,
|
|
786
|
+
publicDescriptionVisible: feature.publicDescriptionVisible ?? null,
|
|
787
|
+
publicDisplayOrder: feature.publicDisplayOrder ?? null
|
|
788
|
+
});
|
|
789
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
790
|
+
res.end(JSON.stringify({ success: true, feature }));
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
const statusCode = resolveErrorStatusCode(error, 400);
|
|
794
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
795
|
+
res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to create feature catalog entry'), code: error?.code }));
|
|
796
|
+
}
|
|
797
|
+
});
|
|
715
798
|
addRoute('PATCH', '/api/platform/features/:featureKey', async (req, res, params) => {
|
|
716
799
|
if (!ensureCloudPlanData(req, res))
|
|
717
800
|
return;
|
|
@@ -722,15 +805,34 @@ export function registerAdminRoutes(deps) {
|
|
|
722
805
|
}
|
|
723
806
|
const body = await parseJsonBody(req);
|
|
724
807
|
const featureKey = String(params.featureKey || '').trim();
|
|
808
|
+
const label = body?.label === undefined || body?.label === null
|
|
809
|
+
? body?.label
|
|
810
|
+
: String(body.label);
|
|
811
|
+
const description = body?.description === undefined || body?.description === null
|
|
812
|
+
? body?.description
|
|
813
|
+
: String(body.description);
|
|
725
814
|
const publicLabel = body?.publicLabel === undefined || body?.publicLabel === null
|
|
726
815
|
? body?.publicLabel
|
|
727
816
|
: String(body.publicLabel);
|
|
728
817
|
const publicDescription = body?.publicDescription === undefined || body?.publicDescription === null
|
|
729
818
|
? body?.publicDescription
|
|
730
819
|
: String(body.publicDescription);
|
|
820
|
+
const publicDescriptionVisible = body?.publicDescriptionVisible === undefined || body?.publicDescriptionVisible === null
|
|
821
|
+
? body?.publicDescriptionVisible
|
|
822
|
+
: (body.publicDescriptionVisible === false ? false : true);
|
|
731
823
|
const publicDisplayOrder = body?.publicDisplayOrder === undefined || body?.publicDisplayOrder === null || body?.publicDisplayOrder === ''
|
|
732
824
|
? null
|
|
733
825
|
: Math.max(0, Math.floor(Number(body.publicDisplayOrder)));
|
|
826
|
+
if (typeof label === 'string' && label.trim().length > 120) {
|
|
827
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
828
|
+
res.end(JSON.stringify({ success: false, error: 'Feature label must be 120 characters or fewer.', code: 'PLAN_FEATURE_LABEL_TOO_LONG' }));
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (typeof description === 'string' && description.trim().length > 280) {
|
|
832
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
833
|
+
res.end(JSON.stringify({ success: false, error: 'Feature description must be 280 characters or fewer.', code: 'PLAN_FEATURE_DESCRIPTION_TOO_LONG' }));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
734
836
|
if (typeof publicLabel === 'string' && publicLabel.trim().length > 120) {
|
|
735
837
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
736
838
|
res.end(JSON.stringify({ success: false, error: 'Public label must be 120 characters or fewer.', code: 'PLAN_FEATURE_PUBLIC_LABEL_TOO_LONG' }));
|
|
@@ -749,14 +851,20 @@ export function registerAdminRoutes(deps) {
|
|
|
749
851
|
try {
|
|
750
852
|
const feature = core.updatePlanFeatureCatalogMarketingCopy({
|
|
751
853
|
featureKey,
|
|
854
|
+
label,
|
|
855
|
+
description,
|
|
752
856
|
publicLabel,
|
|
753
857
|
publicDescription,
|
|
858
|
+
publicDescriptionVisible,
|
|
754
859
|
publicDisplayOrder
|
|
755
860
|
});
|
|
756
861
|
auditAdminAction(req, 'platform-plan-feature-catalog-updated', {
|
|
757
862
|
featureKey,
|
|
863
|
+
label: feature.label,
|
|
864
|
+
description: feature.description,
|
|
758
865
|
publicLabel: feature.publicLabel ?? null,
|
|
759
866
|
publicDescription: feature.publicDescription ?? null,
|
|
867
|
+
publicDescriptionVisible: feature.publicDescriptionVisible ?? null,
|
|
760
868
|
publicDisplayOrder: feature.publicDisplayOrder ?? null
|
|
761
869
|
});
|
|
762
870
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
@@ -768,6 +876,27 @@ export function registerAdminRoutes(deps) {
|
|
|
768
876
|
res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to update feature catalog entry'), code: error?.code }));
|
|
769
877
|
}
|
|
770
878
|
});
|
|
879
|
+
addRoute('DELETE', '/api/platform/features/:featureKey', async (req, res, params) => {
|
|
880
|
+
if (!ensureCloudPlanData(req, res))
|
|
881
|
+
return;
|
|
882
|
+
if (!ensureAdminRole(req)) {
|
|
883
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
884
|
+
res.end(JSON.stringify({ error: 'Forbidden: system admin role required.' }));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const featureKey = String(params.featureKey || '').trim();
|
|
888
|
+
try {
|
|
889
|
+
core.deleteCustomPlanFeatureCatalogEntry(featureKey);
|
|
890
|
+
auditAdminAction(req, 'platform-plan-feature-catalog-deleted', { featureKey });
|
|
891
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
892
|
+
res.end(JSON.stringify({ success: true }));
|
|
893
|
+
}
|
|
894
|
+
catch (error) {
|
|
895
|
+
const statusCode = resolveErrorStatusCode(error, 400);
|
|
896
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
897
|
+
res.end(JSON.stringify({ success: false, error: String(error?.message || 'Unable to delete feature catalog entry'), code: error?.code }));
|
|
898
|
+
}
|
|
899
|
+
});
|
|
771
900
|
addRoute('POST', '/api/platform/plans/:planId/versions', async (req, res, params) => {
|
|
772
901
|
if (!ensureCloudPlanData(req, res))
|
|
773
902
|
return;
|
|
@@ -1298,6 +1427,7 @@ export function registerAdminRoutes(deps) {
|
|
|
1298
1427
|
...(siteSettings.siteTheme === 'light' || siteSettings.siteTheme === 'dark' ? { siteTheme: siteSettings.siteTheme } : {}),
|
|
1299
1428
|
...(typeof siteSettings.pricingPageTitle === 'string' ? { pricingPageTitle: siteSettings.pricingPageTitle } : {}),
|
|
1300
1429
|
...(typeof siteSettings.pricingPageDescription === 'string' ? { pricingPageDescription: siteSettings.pricingPageDescription } : {}),
|
|
1430
|
+
...(typeof siteSettings.pricingPageShowPublicDescriptions === 'boolean' ? { pricingPageShowPublicDescriptions: siteSettings.pricingPageShowPublicDescriptions } : {}),
|
|
1301
1431
|
onboardingPolicyMode: onboardingPolicy.mode,
|
|
1302
1432
|
onboardingPlanId: onboardingPolicy.planId
|
|
1303
1433
|
},
|
|
@@ -1314,6 +1444,7 @@ export function registerAdminRoutes(deps) {
|
|
|
1314
1444
|
siteTheme: effectiveSite.siteTheme,
|
|
1315
1445
|
pricingPageTitle: effectiveSite.pricingPageTitle,
|
|
1316
1446
|
pricingPageDescription: effectiveSite.pricingPageDescription,
|
|
1447
|
+
pricingPageShowPublicDescriptions: effectiveSite.pricingPageShowPublicDescriptions,
|
|
1317
1448
|
onboardingPolicyMode: effective.onboardingPolicyMode,
|
|
1318
1449
|
onboardingPlanId: effective.onboardingPlanId
|
|
1319
1450
|
}
|
|
@@ -1347,6 +1478,9 @@ export function registerAdminRoutes(deps) {
|
|
|
1347
1478
|
: {}),
|
|
1348
1479
|
...(typeof siteSettings.pricingPageDescription === 'string'
|
|
1349
1480
|
? { pricingPageDescription: siteSettings.pricingPageDescription }
|
|
1481
|
+
: {}),
|
|
1482
|
+
...(typeof siteSettings.pricingPageShowPublicDescriptions === 'boolean'
|
|
1483
|
+
? { pricingPageShowPublicDescriptions: siteSettings.pricingPageShowPublicDescriptions }
|
|
1350
1484
|
: {})
|
|
1351
1485
|
},
|
|
1352
1486
|
status: {
|
|
@@ -1355,7 +1489,8 @@ export function registerAdminRoutes(deps) {
|
|
|
1355
1489
|
animatedBackgroundEnabled: siteBehavior.animatedBackgroundEnabled,
|
|
1356
1490
|
siteTheme: siteBehavior.siteTheme,
|
|
1357
1491
|
pricingPageTitle: siteBehavior.pricingPageTitle,
|
|
1358
|
-
pricingPageDescription: siteBehavior.pricingPageDescription
|
|
1492
|
+
pricingPageDescription: siteBehavior.pricingPageDescription,
|
|
1493
|
+
pricingPageShowPublicDescriptions: siteBehavior.pricingPageShowPublicDescriptions
|
|
1359
1494
|
}
|
|
1360
1495
|
}));
|
|
1361
1496
|
};
|
|
@@ -1990,9 +2125,9 @@ export function registerAdminRoutes(deps) {
|
|
|
1990
2125
|
res.end(JSON.stringify({ error: 'Pricing page title must be 120 characters or fewer.', code: 'PRICING_PAGE_TITLE_TOO_LONG' }));
|
|
1991
2126
|
return;
|
|
1992
2127
|
}
|
|
1993
|
-
if (pricingPageDescription !== undefined && pricingPageDescription.length >
|
|
2128
|
+
if (pricingPageDescription !== undefined && pricingPageDescription.length > PRICING_PAGE_DESCRIPTION_MAX_LENGTH) {
|
|
1994
2129
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
1995
|
-
res.end(JSON.stringify({ error:
|
|
2130
|
+
res.end(JSON.stringify({ error: `Pricing page description must be ${PRICING_PAGE_DESCRIPTION_MAX_LENGTH} characters or fewer.`, code: 'PRICING_PAGE_DESCRIPTION_TOO_LONG' }));
|
|
1996
2131
|
return;
|
|
1997
2132
|
}
|
|
1998
2133
|
const validation = core.validateOnboardingPolicy({
|
|
@@ -2022,6 +2157,9 @@ export function registerAdminRoutes(deps) {
|
|
|
2022
2157
|
: {}),
|
|
2023
2158
|
...(pricingPageDescription !== undefined
|
|
2024
2159
|
? { pricingPageDescription }
|
|
2160
|
+
: {}),
|
|
2161
|
+
...(typeof body?.pricingPageShowPublicDescriptions === 'boolean'
|
|
2162
|
+
? { pricingPageShowPublicDescriptions: body.pricingPageShowPublicDescriptions }
|
|
2025
2163
|
: {})
|
|
2026
2164
|
};
|
|
2027
2165
|
core.updateGlobalSettings({
|
|
@@ -2069,9 +2207,9 @@ export function registerAdminRoutes(deps) {
|
|
|
2069
2207
|
res.end(JSON.stringify({ error: 'Pricing page title must be 120 characters or fewer.', code: 'PRICING_PAGE_TITLE_TOO_LONG' }));
|
|
2070
2208
|
return;
|
|
2071
2209
|
}
|
|
2072
|
-
if (pricingPageDescription !== undefined && pricingPageDescription.length >
|
|
2210
|
+
if (pricingPageDescription !== undefined && pricingPageDescription.length > PRICING_PAGE_DESCRIPTION_MAX_LENGTH) {
|
|
2073
2211
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
2074
|
-
res.end(JSON.stringify({ error:
|
|
2212
|
+
res.end(JSON.stringify({ error: `Pricing page description must be ${PRICING_PAGE_DESCRIPTION_MAX_LENGTH} characters or fewer.`, code: 'PRICING_PAGE_DESCRIPTION_TOO_LONG' }));
|
|
2075
2213
|
return;
|
|
2076
2214
|
}
|
|
2077
2215
|
core.updateGlobalSettings({
|
|
@@ -2093,6 +2231,9 @@ export function registerAdminRoutes(deps) {
|
|
|
2093
2231
|
: {}),
|
|
2094
2232
|
...(pricingPageDescription !== undefined
|
|
2095
2233
|
? { pricingPageDescription }
|
|
2234
|
+
: {}),
|
|
2235
|
+
...(typeof body?.pricingPageShowPublicDescriptions === 'boolean'
|
|
2236
|
+
? { pricingPageShowPublicDescriptions: body.pricingPageShowPublicDescriptions }
|
|
2096
2237
|
: {})
|
|
2097
2238
|
}
|
|
2098
2239
|
});
|
|
@@ -28,6 +28,7 @@ export declare function buildSiteBehavior(core: TaskforceCore): {
|
|
|
28
28
|
siteTheme: string;
|
|
29
29
|
pricingPageTitle: string;
|
|
30
30
|
pricingPageDescription: string;
|
|
31
|
+
pricingPageShowPublicDescriptions: boolean;
|
|
31
32
|
};
|
|
32
33
|
export declare function shouldExposeDevAuthTokens(): boolean;
|
|
33
34
|
export declare function resolveRuntimeConfigBaseUrl(req: IncomingMessage): string;
|
|
@@ -64,7 +64,8 @@ export function buildSiteBehavior(core) {
|
|
|
64
64
|
animatedBackgroundEnabled: resolveBooleanSetting(siteSettings.animatedBackgroundEnabled, false),
|
|
65
65
|
siteTheme,
|
|
66
66
|
pricingPageTitle,
|
|
67
|
-
pricingPageDescription
|
|
67
|
+
pricingPageDescription,
|
|
68
|
+
pricingPageShowPublicDescriptions: resolveBooleanSetting(siteSettings.pricingPageShowPublicDescriptions, false)
|
|
68
69
|
};
|
|
69
70
|
}
|
|
70
71
|
export function shouldExposeDevAuthTokens() {
|
|
@@ -3,6 +3,7 @@ import type { TaskforceCore } from '../../core/Taskforce.js';
|
|
|
3
3
|
import { type RouteHandler, type RouteContext } from './primitives.js';
|
|
4
4
|
export type BillingInterval = 'month' | 'year';
|
|
5
5
|
export type BillingPricingType = 'stripe' | 'free';
|
|
6
|
+
export type BillingPricingAudience = 'public' | 'campaign';
|
|
6
7
|
export interface BillingRouteDependencies {
|
|
7
8
|
addRoute: (method: string, path: string, handler: RouteHandler) => void;
|
|
8
9
|
core: TaskforceCore;
|