nolo-cli 0.1.30 → 0.1.31
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/agent-runtime/agentRecordConfig.ts +0 -2
- package/agent-runtime/hostAdapter.ts +0 -1
- package/agent-runtime/hostRuntimeCapabilities.ts +7 -0
- package/agent-runtime/hostRuntimeConfig.ts +86 -0
- package/agent-runtime/index.ts +13 -0
- package/agent-runtime/localToolPolicy.ts +13 -6
- package/agent-runtime/localWorkspaceTools.ts +333 -30
- package/agent-runtime/platformChatProvider.ts +4 -1
- package/agent-runtime/runtimeToolPolicy.ts +32 -47
- package/agent-runtime/shellCommandPolicy.ts +117 -0
- package/agent-runtime/toolGate.ts +38 -0
- package/agentRecordCommands.ts +1 -1
- package/agentRecordHelpers.ts +35 -20
- package/agentRunParsing.ts +18 -5
- package/agentRuntimeLocal.ts +10 -0
- package/ai/agent/agentRunDialogLineage.ts +56 -0
- package/ai/agent/agentSlice.ts +45 -10
- package/ai/agent/avatarUtils.ts +1 -1
- package/ai/agent/buildSystemPrompt.ts +1 -2
- package/ai/agent/cliExecutor.ts +330 -19
- package/ai/agent/createAgentSchema.ts +22 -0
- package/ai/agent/hooks/fetchPublicAgents.ts +14 -39
- package/ai/agent/hooks/usePublicAgents.ts +90 -192
- package/ai/agent/publicAgentCatalog.ts +359 -0
- package/ai/agent/publicAgentIdentity.ts +76 -0
- package/ai/agent/server/fetchPublicAgents.ts +21 -46
- package/ai/agent/serverOrigin.ts +7 -0
- package/ai/agent/streamAgentChatTurn.ts +23 -21
- package/ai/agent/streamAgentChatTurnUtils.ts +292 -6
- package/ai/agent/types.ts +2 -0
- package/ai/agent/web/agentDisplayUtils.ts +171 -0
- package/ai/agent/web/useAgentModelAvatarComponent.ts +32 -0
- package/ai/ai.locale.ts +8 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +15 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +93 -1
- package/ai/chat/sendOpenAIResponseRequest.ts +86 -9
- package/ai/chat/updateTotalUsage.ts +7 -0
- package/ai/llm/deepinfra.ts +30 -15
- package/ai/llm/mistral.ts +3 -4
- package/ai/llm/providers.ts +2 -0
- package/ai/llm/types.ts +15 -0
- package/ai/memory/capture.ts +3 -3
- package/ai/memory/consolidate.ts +2 -2
- package/ai/memory/delete.ts +4 -3
- package/ai/memory/query.ts +3 -3
- package/ai/memory/remember.ts +2 -2
- package/ai/memory/store.ts +3 -3
- package/ai/memory/storeShared.ts +3 -2
- package/ai/policy/commercialMultiplier.ts +16 -0
- package/ai/token/billingAdminAuditEvent.ts +68 -0
- package/ai/token/billingAdminAuditEventWriter.ts +41 -0
- package/ai/token/billingAnomaly.ts +90 -0
- package/ai/token/billingAnomalyLifecycle.ts +54 -0
- package/ai/token/billingAnomalyLifecycleWriter.ts +44 -0
- package/ai/token/billingAnomalyWriter.ts +41 -0
- package/ai/token/calculatePrice.ts +3 -2
- package/ai/token/deepinfraReconciliation.ts +153 -0
- package/ai/token/deepinfraRequestIdQuery.ts +92 -0
- package/ai/token/externalToolCost.ts +54 -41
- package/ai/token/missingUsageEstimate.ts +12 -3
- package/ai/token/normalizeUsage.ts +45 -5
- package/ai/token/openaiReconciliation.ts +188 -0
- package/ai/token/providerBillableEvent.ts +84 -0
- package/ai/token/providerBillableEventWriter.ts +36 -0
- package/ai/token/providerBillingAnomalyDrilldown.ts +308 -0
- package/ai/token/providerBillingDrilldown.ts +361 -0
- package/ai/token/providerBillingHealthReport.ts +366 -0
- package/ai/token/providerCall.ts +144 -0
- package/ai/token/providerCallSweeper.ts +168 -0
- package/ai/token/providerCallWriter.ts +33 -0
- package/ai/token/providerCredential.ts +89 -0
- package/ai/token/providerCredentialAnomaly.ts +154 -0
- package/ai/token/providerCredentialLifecycle.ts +59 -0
- package/ai/token/providerCredentialLifecycleWriter.ts +44 -0
- package/ai/token/providerCredentialRegistry.ts +83 -0
- package/ai/token/providerCredentialRegistryCommand.ts +97 -0
- package/ai/token/providerCredentialRegistryList.ts +124 -0
- package/ai/token/providerCredentialRegistryResolver.ts +150 -0
- package/ai/token/providerCredentialRegistryWriter.ts +41 -0
- package/ai/token/providerCredentialRotationCommand.ts +102 -0
- package/ai/token/providerCredentialRuntime.ts +56 -0
- package/ai/token/providerDispatchIntent.ts +67 -0
- package/ai/token/providerDispatchIntentWriter.ts +36 -0
- package/ai/token/providerGateway.ts +260 -0
- package/ai/token/providerReconciliation.ts +148 -0
- package/ai/token/providerReconciliationLocal.ts +97 -0
- package/ai/token/providerReconciliationRunner.ts +165 -0
- package/ai/token/providerReconciliationWriter.ts +41 -0
- package/ai/token/ratingResult.ts +202 -0
- package/ai/token/ratingResultWriter.ts +33 -0
- package/ai/token/serverDialogProjection.ts +5 -3
- package/ai/token/serverTokenWriter.ts +11 -3
- package/ai/token/types.ts +10 -0
- package/ai/tools/agent/callAgentTool.ts +3 -4
- package/ai/tools/agent/presets/appBuilderPreset.ts +5 -7
- package/ai/tools/agent/runStreamingAgentTool.ts +1 -2
- package/ai/tools/agent/streamParallelAgentsTool.ts +2 -3
- package/ai/tools/readXPostTool.ts +1 -5
- package/app/admin/adminPages.ts +9 -0
- package/app/i18n/i18n.config.ts +5 -0
- package/app/i18n/translations/pricing.locale.ts +397 -0
- package/app/utils/desktopAgentRuntimeStatusClient.ts +5 -4
- package/app/utils/desktopAgentRuntimeTurnClient.ts +4 -1
- package/app/utils/imageUtils.ts +64 -34
- package/auth/adminPermissions.ts +18 -0
- package/auth/authSlice.ts +26 -12
- package/auth/hooks/useFetchUsers.ts +3 -0
- package/auth/routes.ts +46 -0
- package/auth/server/accessActivity.ts +4 -3
- package/auth/server/adminPermissions.ts +43 -0
- package/auth/server/adminUsageReport.ts +375 -0
- package/auth/server/billingAnomalies.ts +235 -0
- package/auth/server/billingUsageReport.ts +41 -2
- package/auth/server/creatorEarningCreditConversion.ts +10 -2
- package/auth/server/deduct.ts +14 -7
- package/auth/server/delete.ts +10 -2
- package/auth/server/disable.ts +4 -3
- package/auth/server/emailAutomation.ts +7 -5
- package/auth/server/emailDbConfig.ts +4 -2
- package/auth/server/emailDelivery.ts +24 -20
- package/auth/server/emailDeliveryGuards.ts +10 -7
- package/auth/server/emailPreferences.ts +6 -4
- package/auth/server/emailReport.ts +6 -4
- package/auth/server/emailRuntimeConfig.ts +3 -3
- package/auth/server/emailScenarioNotifications.ts +9 -8
- package/auth/server/enable.ts +4 -3
- package/auth/server/getRechargeHistory.ts +3 -2
- package/auth/server/getUser.ts +3 -2
- package/auth/server/gptProAccess.ts +44 -0
- package/auth/server/inviteReward.ts +10 -7
- package/auth/server/listusers.ts +6 -4
- package/auth/server/login.ts +2 -2
- package/auth/server/productEntitlements.ts +7 -4
- package/auth/server/productPurchase.ts +10 -2
- package/auth/server/providerBillingDrilldown.ts +47 -0
- package/auth/server/providerBillingHealth.ts +71 -0
- package/auth/server/providerCredentials.ts +136 -0
- package/auth/server/recharge.ts +14 -8
- package/auth/server/revokeSessions.ts +4 -3
- package/auth/server/route.ts +77 -0
- package/auth/server/sendEmail.ts +5 -4
- package/auth/server/signup.ts +10 -3
- package/auth/server/token.ts +53 -55
- package/auth/server/tokenUsageBilling.ts +18 -7
- package/auth/server/usageManagementAccess.ts +43 -0
- package/auth/server/usageReport.ts +59 -0
- package/auth/types.ts +12 -0
- package/auth/utils.ts +24 -22
- package/authCommands.ts +36 -2
- package/buildPublish.ts +3 -1
- package/buildPublishArtifactAssembly.ts +62 -1
- package/buildPublishImportAnalysis.ts +21 -16
- package/chat/dialog/dialogLaunchScope.ts +32 -0
- package/chat/dialog/objectAssistantRegistry.ts +2 -2
- package/chat/dialog/useCreateDialog.ts +1 -1
- package/chat/hooks/useChatInput.ts +41 -10
- package/chat/messages/messageSlice.ts +5 -1
- package/chat/messages/pendingAttachmentParts.ts +28 -2
- package/chat/messages/sendFirstMessage.ts +76 -79
- package/chat/messages/types.ts +9 -0
- package/chat/messages/web/streamingMarkdownModel.ts +41 -0
- package/chat/messages/web/useStreamingReveal.ts +91 -0
- package/chat/web/messageInputAgentUi.ts +94 -6
- package/cliEnvHelpers.ts +8 -0
- package/cliRuntimeDoctorReport.ts +193 -0
- package/client/agentConfigResolver.test.ts +0 -2
- package/client/agentRun.test.ts +171 -10
- package/client/agentRun.ts +44 -637
- package/client/agentRunInput.ts +10 -0
- package/client/agentRunLocalExecutor.ts +179 -0
- package/client/agentRunServerTransport.ts +394 -0
- package/client/agentRunTaskRunRecorder.ts +176 -0
- package/client/localRuntimeAdapter.test.ts +236 -9
- package/client/localRuntimeAdapter.ts +221 -23
- package/client/localToolPolicy.test.ts +28 -4
- package/connector-experimental/daemonLauncher.ts +38 -4
- package/connectorRunArtifact.ts +34 -10
- package/connectorWebSocketTarget.ts +9 -5
- package/core/builtinAgents.ts +155 -11
- package/create/space/contentLabels.ts +1 -1
- package/create/space/contentTypeMeta.ts +3 -2
- package/create/space/sidebarVisibleTypes.ts +27 -0
- package/create/space/space.locale.ts +76 -4
- package/database/actions/agentReadResolution.ts +2 -8
- package/database/actions/read.ts +11 -9
- package/database/actions/readAndWait.ts +10 -5
- package/database/server/agentDelegation.ts +3 -2
- package/database/server/authorityStoreConformance.ts +209 -0
- package/database/server/authorityStoreTypes.ts +31 -0
- package/database/server/coreDataProxy.ts +1 -1
- package/database/server/dataHandlers.ts +13 -5
- package/database/server/db.ts +15 -34
- package/database/server/delete.ts +88 -71
- package/database/server/emailRepository.ts +11 -9
- package/database/server/fileService.ts +2 -1
- package/database/server/legacyServerDb.ts +58 -0
- package/database/server/levelAuthorityStore.ts +75 -0
- package/database/server/memoryAuthorityStore.ts +133 -0
- package/database/server/patch.ts +13 -12
- package/database/server/query.ts +3 -2
- package/database/server/read.ts +33 -30
- package/database/server/resourceAccess.ts +7 -5
- package/database/server/serverStoreFactory.ts +124 -0
- package/database/server/spaceMemberAuthority.ts +3 -2
- package/database/server/storeBatchCompat.ts +37 -0
- package/database/server/testAuthorityStore.ts +76 -0
- package/database/server/write.ts +29 -27
- package/integrations/anthropic/anthropicModels.ts +12 -12
- package/integrations/deepseek/models.ts +67 -67
- package/integrations/google/models.ts +111 -53
- package/integrations/openai/models.ts +22 -22
- package/integrations/xai/models.ts +1 -1
- package/localRuntimeDb.ts +39 -2
- package/machineCommandPrompts.ts +2 -0
- package/machineCommands.ts +193 -63
- package/machineWsRunDispatch.ts +80 -15
- package/machineWsSession.ts +39 -2
- package/noloReadBridgePrompt.ts +16 -0
- package/package.json +99 -6
- package/render/layout/mainLayoutViewMode.ts +25 -0
- package/render/page/server/createPage.ts +3 -1
- package/render/page/server/handleCreatePage.ts +2 -11
- package/runtimeDoctorCommands.ts +15 -66
- package/server/api/methods.ts +34 -12
- package/server/api/tableHandlers.ts +6 -3
- package/server/authenticateToken.ts +47 -2
- package/server/billingLedgerRoutes.ts +15 -0
- package/server/buildManifest.ts +54 -0
- package/server/coreReadiness.ts +34 -25
- package/server/coreRuntime.ts +15 -10
- package/server/handlers/activityStatsHandler.ts +5 -2
- package/server/handlers/agentEmailRegistrationE2EHandler.ts +25 -23
- package/server/handlers/agentRun/agentLookup.ts +1 -7
- package/server/handlers/agentRun/autonomousRuntime.ts +11 -10
- package/server/handlers/agentRun/builtinAgents.ts +12 -88
- package/server/handlers/agentRun/continueDialogContext.ts +15 -3
- package/server/handlers/agentRun/{inheritedDialogPrompt.ts → conversationReference.ts} +3 -3
- package/server/handlers/agentRun/dialogLookup.ts +4 -3
- package/server/handlers/agentRun/index.ts +864 -404
- package/server/handlers/agentRun/localRuntimeBootstrap.ts +17 -1
- package/server/handlers/agentRun/loop.ts +920 -508
- package/server/handlers/agentRun/machineConnector.ts +56 -8
- package/server/handlers/agentRun/machineRunAudit.ts +3 -2
- package/server/handlers/agentRun/modelUsageToolHandlers.ts +13 -10
- package/server/handlers/agentRun/requestContext.ts +12 -8
- package/server/handlers/agentRun/runtimeExecutionContext.ts +15 -3
- package/server/handlers/agentRun/runtimePreparation.ts +26 -24
- package/server/handlers/agentRun/runtimeRouting.ts +25 -40
- package/server/handlers/agentRun/runtimeSystemMessages.ts +65 -0
- package/server/handlers/agentRun/saveDialog.ts +97 -87
- package/server/handlers/agentRun/taskRunToolHandler.ts +9 -7
- package/server/handlers/agentRun/toolExecutor.ts +42 -78
- package/server/handlers/agentRun/types.ts +2 -1
- package/server/handlers/appDeployStatusStore.ts +17 -10
- package/server/handlers/appDomainHandler.ts +21 -15
- package/server/handlers/appLegacyInfraCleanup.ts +5 -2
- package/server/handlers/appSandboxHandler.ts +29 -8
- package/server/handlers/appSpaceAccess.ts +15 -8
- package/server/handlers/chatHandler.ts +22 -2
- package/server/handlers/chatUpstreamRetry.ts +225 -2
- package/server/handlers/connector/connectorConnectionRegistry.ts +55 -7
- package/server/handlers/connector/connectorWebSocket.ts +14 -10
- package/server/handlers/desktopAgentRuntimeAdapter.ts +4 -4
- package/server/handlers/desktopAgentRuntimeHostCapabilities.ts +25 -0
- package/server/handlers/desktopAgentRuntimeReadiness.ts +62 -0
- package/server/handlers/desktopAgentRuntimeStatusHandler.ts +38 -30
- package/server/handlers/desktopAgentRuntimeTurnHandler.ts +7 -3
- package/server/handlers/desktopAgentRuntimeTurnService.ts +39 -45
- package/server/handlers/desktopLlamaRuntimeHandler.ts +85 -51
- package/server/handlers/desktopUpdaterHandler.ts +132 -24
- package/server/handlers/favoriteHandlers.ts +8 -2
- package/server/handlers/getDialogBillingHandler.ts +4 -3
- package/server/handlers/getTransactionsHandler.ts +5 -2
- package/server/handlers/machines/machineBinding.ts +1 -0
- package/server/handlers/machines/machineHandlers.ts +0 -14
- package/server/handlers/machines/machineInstallAssets.ts +4 -2
- package/server/handlers/notificationEmitter.ts +5 -3
- package/server/handlers/notificationsHandler.ts +5 -2
- package/server/handlers/providerCallsRecentHandler.ts +234 -0
- package/server/handlers/providerReconciliationRunHandler.ts +180 -0
- package/server/handlers/providerRequestIdsHandler.ts +105 -0
- package/server/handlers/tableAccess.ts +6 -2
- package/server/handlers/tableToolHandlers.ts +28 -14
- package/server/handlers/userSecretsHandler.ts +17 -6
- package/server/handlers/versionHandler.ts +9 -4
- package/server/handlers/versionStore.ts +18 -10
- package/server/html/ssrPreload.ts +5 -3
- package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +11 -8
- package/server/payments/crypto/cryptoUsdcBaseScanner.ts +12 -7
- package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +11 -8
- package/server/payments/crypto/cryptoUsdtTronScanner.ts +9 -7
- package/server/payments/waffo/waffoPaymentProvider.ts +12 -9
- package/server/runtimeRole.ts +15 -0
- package/server/scheduler/scheduledTaskRuntime.ts +4 -2
- package/server/scheduler/schedulerService.ts +2 -2
- package/server/taskRunBridgeRoutes.ts +8 -5
- package/share/server/fetchCommunityShares.ts +1 -1
- package/share/server/helpers.ts +19 -17
- package/share/server/importShare.ts +9 -13
- package/share/server/listCommunityShares.ts +4 -4
- package/share/server/purchaseDoc.ts +8 -16
- package/share/server/readCommunityShareCover.ts +3 -2
- package/share/server/readSharedTable.ts +14 -13
- package/share/server/tableReplication.ts +42 -39
- package/spaceAttachCommands.ts +135 -0
- package/spaceInternalCommandEntries.ts +2 -0
- package/ai/agent/runtimeServerBase.ts +0 -37
- package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createBillingAdminAuditEvent,
|
|
3
|
+
type BillingAdminAuditAction,
|
|
4
|
+
} from "../../ai/token/billingAdminAuditEvent";
|
|
5
|
+
import { writeBillingAdminAuditEvent } from "../../ai/token/billingAdminAuditEventWriter";
|
|
6
|
+
import type { BillingAnomalyStatus } from "../../ai/token/billingAnomaly";
|
|
7
|
+
import { createBillingAnomalyLifecycleEvent } from "../../ai/token/billingAnomalyLifecycle";
|
|
8
|
+
import { writeBillingAnomalyLifecycleEvent } from "../../ai/token/billingAnomalyLifecycleWriter";
|
|
9
|
+
import { buildProviderBillingAnomalyDrilldown } from "../../ai/token/providerBillingAnomalyDrilldown";
|
|
10
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
11
|
+
import { ulid } from "ulid";
|
|
12
|
+
import {
|
|
13
|
+
createErrorResponse,
|
|
14
|
+
createSuccessResponse,
|
|
15
|
+
handleOptionsRequest,
|
|
16
|
+
} from "./shared";
|
|
17
|
+
import { canManageUsageReportAccess } from "./usageManagementAccess";
|
|
18
|
+
|
|
19
|
+
const serverDb = getServerAuthorityStore();
|
|
20
|
+
|
|
21
|
+
const parseStatus = (value: unknown): BillingAnomalyStatus | undefined => {
|
|
22
|
+
if (
|
|
23
|
+
value === "open" ||
|
|
24
|
+
value === "acknowledged" ||
|
|
25
|
+
value === "resolved" ||
|
|
26
|
+
value === "ignored"
|
|
27
|
+
) {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const parseAuditAction = (value: unknown): BillingAdminAuditAction | undefined => {
|
|
34
|
+
if (
|
|
35
|
+
value === "dry_run_viewed" ||
|
|
36
|
+
value === "plan_marked_reviewed" ||
|
|
37
|
+
value === "plan_abandoned"
|
|
38
|
+
) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const parseLimit = (value: string | null) => {
|
|
45
|
+
const parsed = Number(value);
|
|
46
|
+
if (!Number.isFinite(parsed)) return 50;
|
|
47
|
+
return Math.min(Math.max(Math.floor(parsed), 1), 200);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type BillingAnomalyLifecycleDeps = {
|
|
51
|
+
store?: Parameters<typeof writeBillingAnomalyLifecycleEvent>[0]["store"];
|
|
52
|
+
canManage?: (userId?: string | null) => Promise<boolean>;
|
|
53
|
+
now?: () => string;
|
|
54
|
+
createEventId?: () => string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export async function handleUpdateBillingAnomalyLifecycle(
|
|
58
|
+
req: Request,
|
|
59
|
+
deps: BillingAnomalyLifecycleDeps = {}
|
|
60
|
+
) {
|
|
61
|
+
if (req.method === "OPTIONS") {
|
|
62
|
+
return handleOptionsRequest();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { userId: actionUserId } = req.user;
|
|
66
|
+
const canManage = deps.canManage ?? canManageUsageReportAccess;
|
|
67
|
+
if (!(await canManage(actionUserId))) {
|
|
68
|
+
return createErrorResponse("Unauthorized: Usage management access required", 403);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let body: Record<string, unknown>;
|
|
72
|
+
try {
|
|
73
|
+
body = (await req.json()) as Record<string, unknown>;
|
|
74
|
+
} catch {
|
|
75
|
+
return createErrorResponse("Invalid JSON body", 400);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const anomalyId =
|
|
79
|
+
typeof body.anomalyId === "string" ? body.anomalyId.trim() : "";
|
|
80
|
+
if (!anomalyId) return createErrorResponse("anomalyId is required", 400);
|
|
81
|
+
|
|
82
|
+
const status = parseStatus(body.status);
|
|
83
|
+
if (!status) {
|
|
84
|
+
return createErrorResponse(
|
|
85
|
+
"status must be open, acknowledged, resolved, or ignored",
|
|
86
|
+
400
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const now = deps.now?.() ?? new Date().toISOString();
|
|
91
|
+
const event = createBillingAnomalyLifecycleEvent({
|
|
92
|
+
anomalyId,
|
|
93
|
+
eventId:
|
|
94
|
+
typeof body.eventId === "string" && body.eventId.trim()
|
|
95
|
+
? body.eventId.trim()
|
|
96
|
+
: deps.createEventId?.() ?? `evt_${ulid()}`,
|
|
97
|
+
status,
|
|
98
|
+
createdAt: now,
|
|
99
|
+
actorId: actionUserId,
|
|
100
|
+
reason: typeof body.reason === "string" ? body.reason : undefined,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const result = await writeBillingAnomalyLifecycleEvent({
|
|
105
|
+
store: deps.store ?? serverDb,
|
|
106
|
+
event,
|
|
107
|
+
});
|
|
108
|
+
return createSuccessResponse({
|
|
109
|
+
success: true,
|
|
110
|
+
key: result.key,
|
|
111
|
+
event: {
|
|
112
|
+
anomalyId: event.anomalyId,
|
|
113
|
+
eventId: event.eventId,
|
|
114
|
+
status: event.status,
|
|
115
|
+
createdAt: event.createdAt,
|
|
116
|
+
actorId: event.actorId,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
} catch {
|
|
120
|
+
return createErrorResponse("Internal server error");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
type BillingAnomalyDrilldownDeps = {
|
|
125
|
+
store?: Parameters<typeof buildProviderBillingAnomalyDrilldown>[0]["store"];
|
|
126
|
+
canManage?: (userId?: string | null) => Promise<boolean>;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export async function handleGetBillingAnomalyDrilldown(
|
|
130
|
+
req: Request,
|
|
131
|
+
deps: BillingAnomalyDrilldownDeps = {}
|
|
132
|
+
) {
|
|
133
|
+
if (req.method === "OPTIONS") {
|
|
134
|
+
return handleOptionsRequest();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const { userId: actionUserId } = req.user;
|
|
138
|
+
const canManage = deps.canManage ?? canManageUsageReportAccess;
|
|
139
|
+
if (!(await canManage(actionUserId))) {
|
|
140
|
+
return createErrorResponse("Unauthorized: Usage management access required", 403);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const url = new URL(req.url);
|
|
145
|
+
const anomalyId = url.searchParams.get("anomalyId")?.trim();
|
|
146
|
+
if (!anomalyId) {
|
|
147
|
+
return createErrorResponse("Missing anomalyId", 400);
|
|
148
|
+
}
|
|
149
|
+
const drilldown = await buildProviderBillingAnomalyDrilldown({
|
|
150
|
+
store: deps.store ?? serverDb,
|
|
151
|
+
anomalyId,
|
|
152
|
+
limit: parseLimit(url.searchParams.get("limit")),
|
|
153
|
+
});
|
|
154
|
+
return createSuccessResponse({ success: true, drilldown });
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (
|
|
157
|
+
error instanceof Error &&
|
|
158
|
+
error.message.startsWith("billing anomaly not found:")
|
|
159
|
+
) {
|
|
160
|
+
return createErrorResponse("Billing anomaly not found", 404);
|
|
161
|
+
}
|
|
162
|
+
return createErrorResponse("Internal server error");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type BillingAdminAuditDeps = {
|
|
167
|
+
store?: Parameters<typeof writeBillingAdminAuditEvent>[0]["store"];
|
|
168
|
+
canManage?: (userId?: string | null) => Promise<boolean>;
|
|
169
|
+
now?: () => string;
|
|
170
|
+
createEventId?: () => string;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export async function handleCreateBillingAdminAuditEvent(
|
|
174
|
+
req: Request,
|
|
175
|
+
deps: BillingAdminAuditDeps = {}
|
|
176
|
+
) {
|
|
177
|
+
if (req.method === "OPTIONS") {
|
|
178
|
+
return handleOptionsRequest();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const { userId: actionUserId } = req.user;
|
|
182
|
+
const canManage = deps.canManage ?? canManageUsageReportAccess;
|
|
183
|
+
if (!(await canManage(actionUserId))) {
|
|
184
|
+
return createErrorResponse("Unauthorized: Usage management access required", 403);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let body: Record<string, unknown>;
|
|
188
|
+
try {
|
|
189
|
+
body = (await req.json()) as Record<string, unknown>;
|
|
190
|
+
} catch {
|
|
191
|
+
return createErrorResponse("Invalid JSON body", 400);
|
|
192
|
+
}
|
|
193
|
+
const action = parseAuditAction(body.action);
|
|
194
|
+
if (!action) {
|
|
195
|
+
return createErrorResponse(
|
|
196
|
+
"action must be dry_run_viewed, plan_marked_reviewed, or plan_abandoned",
|
|
197
|
+
400
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const event = createBillingAdminAuditEvent({
|
|
201
|
+
eventId:
|
|
202
|
+
typeof body.eventId === "string" && body.eventId.trim()
|
|
203
|
+
? body.eventId.trim()
|
|
204
|
+
: deps.createEventId?.() ?? `evt_${ulid()}`,
|
|
205
|
+
actorId: actionUserId,
|
|
206
|
+
action,
|
|
207
|
+
anomalyId: typeof body.anomalyId === "string" ? body.anomalyId : "",
|
|
208
|
+
planId: typeof body.planId === "string" ? body.planId : "",
|
|
209
|
+
inputSetHash: typeof body.inputSetHash === "string" ? body.inputSetHash : "",
|
|
210
|
+
createdAt: deps.now?.() ?? new Date().toISOString(),
|
|
211
|
+
reason: typeof body.reason === "string" ? body.reason : undefined,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const result = await writeBillingAdminAuditEvent({
|
|
216
|
+
store: deps.store ?? serverDb,
|
|
217
|
+
event,
|
|
218
|
+
});
|
|
219
|
+
return createSuccessResponse({
|
|
220
|
+
success: true,
|
|
221
|
+
key: result.key,
|
|
222
|
+
event: {
|
|
223
|
+
eventId: event.eventId,
|
|
224
|
+
actorId: event.actorId,
|
|
225
|
+
action: event.action,
|
|
226
|
+
anomalyId: event.anomalyId,
|
|
227
|
+
planId: event.planId,
|
|
228
|
+
inputSetHash: event.inputSetHash,
|
|
229
|
+
createdAt: event.createdAt,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
} catch {
|
|
233
|
+
return createErrorResponse("Internal server error");
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -19,20 +19,31 @@ export type BillingUsageReport = {
|
|
|
19
19
|
titleCostRatio: number;
|
|
20
20
|
inputTokens: number;
|
|
21
21
|
outputTokens: number;
|
|
22
|
+
cacheCreationInputTokens: number;
|
|
23
|
+
cacheReadInputTokens: number;
|
|
22
24
|
};
|
|
23
25
|
byCybot: BillingUsageGroup[];
|
|
26
|
+
byUser: BillingUsageGroup[];
|
|
27
|
+
byDialog: BillingUsageGroup[];
|
|
24
28
|
byModel: BillingUsageGroup[];
|
|
25
29
|
byProvider: BillingUsageGroup[];
|
|
30
|
+
byDay: BillingUsageGroup[];
|
|
26
31
|
};
|
|
27
32
|
|
|
28
33
|
export type BillingUsageGroup = {
|
|
29
34
|
cybotId?: string;
|
|
35
|
+
userId?: string;
|
|
36
|
+
username?: string;
|
|
37
|
+
dialogId?: string;
|
|
30
38
|
model?: string;
|
|
31
39
|
provider?: string;
|
|
40
|
+
dateKey?: string;
|
|
32
41
|
count: number;
|
|
33
42
|
cost: number;
|
|
34
43
|
inputTokens: number;
|
|
35
44
|
outputTokens: number;
|
|
45
|
+
cacheCreationInputTokens: number;
|
|
46
|
+
cacheReadInputTokens: number;
|
|
36
47
|
costRatio: number;
|
|
37
48
|
};
|
|
38
49
|
|
|
@@ -40,11 +51,15 @@ type TokenRecord = {
|
|
|
40
51
|
type?: string;
|
|
41
52
|
userId?: string;
|
|
42
53
|
cybotId?: string;
|
|
54
|
+
dialogId?: string;
|
|
43
55
|
provider?: string;
|
|
44
56
|
model?: string;
|
|
45
57
|
input_tokens?: number;
|
|
46
58
|
output_tokens?: number;
|
|
59
|
+
cache_creation_input_tokens?: number;
|
|
60
|
+
cache_read_input_tokens?: number;
|
|
47
61
|
cost?: number;
|
|
62
|
+
dateKey?: string;
|
|
48
63
|
timestamp?: number;
|
|
49
64
|
createdAt?: string;
|
|
50
65
|
};
|
|
@@ -85,6 +100,8 @@ const createAccumulator = () => ({
|
|
|
85
100
|
cost: 0,
|
|
86
101
|
inputTokens: 0,
|
|
87
102
|
outputTokens: 0,
|
|
103
|
+
cacheCreationInputTokens: 0,
|
|
104
|
+
cacheReadInputTokens: 0,
|
|
88
105
|
});
|
|
89
106
|
|
|
90
107
|
type Accumulator = ReturnType<typeof createAccumulator>;
|
|
@@ -94,6 +111,8 @@ const addToAccumulator = (acc: Accumulator, record: TokenRecord) => {
|
|
|
94
111
|
acc.cost += toFiniteNumber(record.cost);
|
|
95
112
|
acc.inputTokens += toFiniteNumber(record.input_tokens);
|
|
96
113
|
acc.outputTokens += toFiniteNumber(record.output_tokens);
|
|
114
|
+
acc.cacheCreationInputTokens += toFiniteNumber(record.cache_creation_input_tokens);
|
|
115
|
+
acc.cacheReadInputTokens += toFiniteNumber(record.cache_read_input_tokens);
|
|
97
116
|
};
|
|
98
117
|
|
|
99
118
|
const addGroupedRecord = (
|
|
@@ -107,7 +126,7 @@ const addGroupedRecord = (
|
|
|
107
126
|
};
|
|
108
127
|
|
|
109
128
|
const formatGroup = (
|
|
110
|
-
keyName: "cybotId" | "model" | "provider",
|
|
129
|
+
keyName: "cybotId" | "userId" | "dialogId" | "model" | "provider" | "dateKey",
|
|
111
130
|
totalCost: number,
|
|
112
131
|
[key, acc]: [string, Accumulator]
|
|
113
132
|
): BillingUsageGroup => ({
|
|
@@ -116,11 +135,13 @@ const formatGroup = (
|
|
|
116
135
|
cost: roundMetric(acc.cost),
|
|
117
136
|
inputTokens: acc.inputTokens,
|
|
118
137
|
outputTokens: acc.outputTokens,
|
|
138
|
+
cacheCreationInputTokens: acc.cacheCreationInputTokens,
|
|
139
|
+
cacheReadInputTokens: acc.cacheReadInputTokens,
|
|
119
140
|
costRatio: totalCost > 0 ? roundMetric(acc.cost / totalCost) : 0,
|
|
120
141
|
});
|
|
121
142
|
|
|
122
143
|
const sortedGroups = (
|
|
123
|
-
keyName: "cybotId" | "model" | "provider",
|
|
144
|
+
keyName: "cybotId" | "userId" | "dialogId" | "model" | "provider" | "dateKey",
|
|
124
145
|
totalCost: number,
|
|
125
146
|
groups: Map<string, Accumulator>
|
|
126
147
|
) =>
|
|
@@ -136,8 +157,11 @@ export async function buildBillingUsageReport(args: {
|
|
|
136
157
|
}): Promise<BillingUsageReport> {
|
|
137
158
|
const totals = createAccumulator();
|
|
138
159
|
const byCybot = new Map<string, Accumulator>();
|
|
160
|
+
const byUser = new Map<string, Accumulator>();
|
|
161
|
+
const byDialog = new Map<string, Accumulator>();
|
|
139
162
|
const byModel = new Map<string, Accumulator>();
|
|
140
163
|
const byProvider = new Map<string, Accumulator>();
|
|
164
|
+
const byDay = new Map<string, Accumulator>();
|
|
141
165
|
let recordsScanned = 0;
|
|
142
166
|
let titleCost = 0;
|
|
143
167
|
let titleCount = 0;
|
|
@@ -157,11 +181,19 @@ export async function buildBillingUsageReport(args: {
|
|
|
157
181
|
addToAccumulator(totals, value);
|
|
158
182
|
|
|
159
183
|
const cybotId = value.cybotId || "unknown";
|
|
184
|
+
const userId = value.userId || "unknown";
|
|
185
|
+
const dialogId = value.dialogId || "unknown";
|
|
160
186
|
const model = value.model || "unknown";
|
|
161
187
|
const provider = value.provider || "unknown";
|
|
188
|
+
const dateKey =
|
|
189
|
+
value.dateKey ||
|
|
190
|
+
(timestamp == null ? "unknown" : new Date(timestamp).toISOString().slice(0, 10));
|
|
162
191
|
addGroupedRecord(byCybot, cybotId, value);
|
|
192
|
+
addGroupedRecord(byUser, userId, value);
|
|
193
|
+
addGroupedRecord(byDialog, dialogId, value);
|
|
163
194
|
addGroupedRecord(byModel, model, value);
|
|
164
195
|
addGroupedRecord(byProvider, provider, value);
|
|
196
|
+
addGroupedRecord(byDay, dateKey, value);
|
|
165
197
|
|
|
166
198
|
if (cybotId === TITLE_CYBOT_ID) {
|
|
167
199
|
titleCount += 1;
|
|
@@ -184,9 +216,16 @@ export async function buildBillingUsageReport(args: {
|
|
|
184
216
|
titleCostRatio: totalCost > 0 ? roundMetric(roundedTitleCost / totalCost) : 0,
|
|
185
217
|
inputTokens: totals.inputTokens,
|
|
186
218
|
outputTokens: totals.outputTokens,
|
|
219
|
+
cacheCreationInputTokens: totals.cacheCreationInputTokens,
|
|
220
|
+
cacheReadInputTokens: totals.cacheReadInputTokens,
|
|
187
221
|
},
|
|
188
222
|
byCybot: sortedGroups("cybotId", totalCost, byCybot),
|
|
223
|
+
byUser: sortedGroups("userId", totalCost, byUser),
|
|
224
|
+
byDialog: sortedGroups("dialogId", totalCost, byDialog),
|
|
189
225
|
byModel: sortedGroups("model", totalCost, byModel),
|
|
190
226
|
byProvider: sortedGroups("provider", totalCost, byProvider),
|
|
227
|
+
byDay: sortedGroups("dateKey", totalCost, byDay).sort((a, b) =>
|
|
228
|
+
String(b.dateKey ?? "").localeCompare(String(a.dateKey ?? ""))
|
|
229
|
+
),
|
|
191
230
|
};
|
|
192
231
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
|
|
3
3
|
import { createTransactionKey, DB_PREFIX } from "../../database/keys";
|
|
4
|
-
import
|
|
4
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
5
|
+
import { writeStoreOps } from "../../database/server/storeBatchCompat";
|
|
5
6
|
import { ulid } from "ulid";
|
|
6
7
|
import { balanceLockManager } from "./locks";
|
|
7
8
|
import { withRetry } from "./utils";
|
|
@@ -31,6 +32,13 @@ export type CreatorEarningCreditConversionStore = {
|
|
|
31
32
|
batch: (ops: StoreOperation[]) => Promise<void>;
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
const createCreatorEarningCreditConversionStore = (
|
|
36
|
+
authorityStore = getServerAuthorityStore()
|
|
37
|
+
): CreatorEarningCreditConversionStore => ({
|
|
38
|
+
get: (key) => authorityStore.get(key),
|
|
39
|
+
batch: (ops) => writeStoreOps(authorityStore, ops),
|
|
40
|
+
});
|
|
41
|
+
|
|
34
42
|
type ConversionRecord = {
|
|
35
43
|
kind: "creator_earning_credit_conversion";
|
|
36
44
|
clusterId: string;
|
|
@@ -148,7 +156,7 @@ const defaultTxId = (clusterId: string, entryId: string) =>
|
|
|
148
156
|
`creator-earning-credit-${clusterId}-${safeKeyPart(entryId)}`;
|
|
149
157
|
|
|
150
158
|
export async function convertCreatorEarningToCredits({
|
|
151
|
-
store =
|
|
159
|
+
store = createCreatorEarningCreditConversionStore(getServerAuthorityStore()),
|
|
152
160
|
runtime = getBillingLedgerRuntime(),
|
|
153
161
|
creatorUserId,
|
|
154
162
|
entryId,
|
package/auth/server/deduct.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// auth/server/deduct.ts
|
|
2
2
|
import { ulid } from "ulid";
|
|
3
|
-
import
|
|
3
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
4
|
+
import { writeStoreOps } from "../../database/server/storeBatchCompat";
|
|
4
5
|
import { createTransactionKey } from "../../database/keys";
|
|
5
6
|
import { logger } from "../server/shared";
|
|
6
7
|
import { balanceLockManager } from "./locks";
|
|
@@ -29,6 +30,12 @@ export async function deductUserBalance(
|
|
|
29
30
|
txId: string = ulid()
|
|
30
31
|
): Promise<DeductResult> {
|
|
31
32
|
const lock = balanceLockManager.getLock(userId);
|
|
33
|
+
const store = getServerAuthorityStore();
|
|
34
|
+
const ledgerStore = {
|
|
35
|
+
get: (key: string) => store.get(key),
|
|
36
|
+
batch: (ops: Array<{ type: "put"; key: string; value: unknown }>) =>
|
|
37
|
+
writeStoreOps(store, ops),
|
|
38
|
+
};
|
|
32
39
|
|
|
33
40
|
return await lock.runExclusive(async () => {
|
|
34
41
|
try {
|
|
@@ -54,7 +61,7 @@ export async function deductUserBalance(
|
|
|
54
61
|
let existingTx = null;
|
|
55
62
|
|
|
56
63
|
try {
|
|
57
|
-
existingTx = await
|
|
64
|
+
existingTx = await store.get(txIndexKey);
|
|
58
65
|
} catch (err) {
|
|
59
66
|
if (!(err instanceof Error && "notFound" in err)) {
|
|
60
67
|
throw err;
|
|
@@ -78,7 +85,7 @@ export async function deductUserBalance(
|
|
|
78
85
|
let userData = null;
|
|
79
86
|
|
|
80
87
|
try {
|
|
81
|
-
userData = await
|
|
88
|
+
userData = await store.get(userKey);
|
|
82
89
|
} catch (err) {
|
|
83
90
|
if (err instanceof Error && "notFound" in err) {
|
|
84
91
|
logger.warn({
|
|
@@ -100,7 +107,7 @@ export async function deductUserBalance(
|
|
|
100
107
|
|
|
101
108
|
const currentBalance = userData.balance;
|
|
102
109
|
const ledgerAppend = await prepareLedgerTransactionAppend({
|
|
103
|
-
store:
|
|
110
|
+
store: ledgerStore,
|
|
104
111
|
type: "usage_charge",
|
|
105
112
|
reason,
|
|
106
113
|
idempotencyKey: txId,
|
|
@@ -167,7 +174,7 @@ export async function deductUserBalance(
|
|
|
167
174
|
|
|
168
175
|
await withRetry(
|
|
169
176
|
async () => {
|
|
170
|
-
await
|
|
177
|
+
await writeStoreOps(store, [
|
|
171
178
|
...ledgerAppend.operations,
|
|
172
179
|
{
|
|
173
180
|
type: "put",
|
|
@@ -229,7 +236,7 @@ export async function deductUserBalance(
|
|
|
229
236
|
let txRecord = null;
|
|
230
237
|
|
|
231
238
|
try {
|
|
232
|
-
txRecord = await
|
|
239
|
+
txRecord = await store.get(txKey);
|
|
233
240
|
} catch (err) {
|
|
234
241
|
if (!(err instanceof Error && "notFound" in err)) {
|
|
235
242
|
throw err;
|
|
@@ -237,7 +244,7 @@ export async function deductUserBalance(
|
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
if (txRecord) {
|
|
240
|
-
await
|
|
247
|
+
await store.put(txKey, {
|
|
241
248
|
...txRecord,
|
|
242
249
|
status: "failed",
|
|
243
250
|
});
|
package/auth/server/delete.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// 文件: auth/deleteUser.ts
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
4
|
+
import { createStoreBatch } from "../../database/server/storeBatchCompat";
|
|
4
5
|
import { authRoutes } from "../routes";
|
|
5
6
|
import { NOLO_CLUSTER_SERVERS } from "../../database/config";
|
|
6
7
|
|
|
@@ -833,6 +834,13 @@ async function deleteUserRelatedData(
|
|
|
833
834
|
}
|
|
834
835
|
}
|
|
835
836
|
|
|
837
|
+
const createDeleteUserDb = (store = getServerAuthorityStore()) => ({
|
|
838
|
+
get: (key: string) => store.get(key),
|
|
839
|
+
del: (key: string) => store.del(key),
|
|
840
|
+
batch: () => createStoreBatch(store),
|
|
841
|
+
iterator: (options: { gte: string; lte: string }) => store.iterator(options),
|
|
842
|
+
});
|
|
843
|
+
|
|
836
844
|
export async function deleteUser(
|
|
837
845
|
db: any,
|
|
838
846
|
userId: string,
|
|
@@ -856,7 +864,7 @@ export async function deleteUser(
|
|
|
856
864
|
export async function handleDeleteUser(
|
|
857
865
|
req: Request,
|
|
858
866
|
userId: string,
|
|
859
|
-
db: any =
|
|
867
|
+
db: any = createDeleteUserDb(getServerAuthorityStore())
|
|
860
868
|
) {
|
|
861
869
|
if (req.method === "OPTIONS") {
|
|
862
870
|
return handleOptionsRequest();
|
package/auth/server/disable.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// auth/server/disable.ts
|
|
2
|
-
import
|
|
2
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
3
3
|
import { DB_PREFIX } from "../../database/keys";
|
|
4
4
|
import {
|
|
5
5
|
createErrorResponse,
|
|
@@ -11,10 +11,11 @@ import { balanceLockManager } from "./locks";
|
|
|
11
11
|
import { notifyAccountStatusChangeEmail } from "./emailScenarioNotifications";
|
|
12
12
|
|
|
13
13
|
async function disableUser(userId: string) {
|
|
14
|
+
const store = getServerAuthorityStore();
|
|
14
15
|
const lock = balanceLockManager.getLock(userId);
|
|
15
16
|
await lock.runExclusive(async () => {
|
|
16
17
|
const userKey = `${DB_PREFIX.USER}${userId}`;
|
|
17
|
-
const userData = await
|
|
18
|
+
const userData = await store.get(userKey).catch(() => null);
|
|
18
19
|
|
|
19
20
|
if (!userData) {
|
|
20
21
|
throw new Error("User not found");
|
|
@@ -26,7 +27,7 @@ async function disableUser(userId: string) {
|
|
|
26
27
|
isDisabled: true,
|
|
27
28
|
disabledAt: new Date().toISOString(), // 记录停用时间,可选
|
|
28
29
|
};
|
|
29
|
-
await
|
|
30
|
+
await store.put(userKey, updatedUserData);
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DB_PREFIX } from "../../database/keys";
|
|
2
|
-
import
|
|
2
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
3
3
|
import { logger } from "./shared";
|
|
4
4
|
import { balanceLockManager } from "./locks";
|
|
5
5
|
import { canSendEmailToUser } from "./emailPolicy";
|
|
@@ -34,9 +34,10 @@ let reengagementRunning = false;
|
|
|
34
34
|
const patchUserRecord = async (userId: string, patch: Record<string, any>) => {
|
|
35
35
|
const key = `${USER_PREFIX}${userId}`;
|
|
36
36
|
const lock = balanceLockManager.getLock(userId);
|
|
37
|
+
const store = getServerAuthorityStore();
|
|
37
38
|
await lock.runExclusive(async () => {
|
|
38
|
-
const current = (await
|
|
39
|
-
await
|
|
39
|
+
const current = (await store.get(key)) as UserEmailRecord;
|
|
40
|
+
await store.put(key, { ...current, ...patch });
|
|
40
41
|
});
|
|
41
42
|
};
|
|
42
43
|
|
|
@@ -115,8 +116,9 @@ export async function runReengagementSweep() {
|
|
|
115
116
|
userId: string;
|
|
116
117
|
user: UserEmailRecord;
|
|
117
118
|
}> = [];
|
|
119
|
+
const store = getServerAuthorityStore();
|
|
118
120
|
|
|
119
|
-
const iter =
|
|
121
|
+
const iter = store.iterator({
|
|
120
122
|
gt: USER_PREFIX,
|
|
121
123
|
lt: USER_PREFIX + "\xFF",
|
|
122
124
|
});
|
|
@@ -295,7 +297,7 @@ export async function runReengagementSweep() {
|
|
|
295
297
|
}
|
|
296
298
|
}
|
|
297
299
|
} finally {
|
|
298
|
-
await iter.close();
|
|
300
|
+
await (iter as any).close?.();
|
|
299
301
|
reengagementRunning = false;
|
|
300
302
|
}
|
|
301
303
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
2
2
|
import { logger } from "./shared";
|
|
3
3
|
|
|
4
4
|
export const EMAIL_RUNTIME_CONFIG_KEY = "config:email:runtime";
|
|
@@ -109,7 +109,9 @@ export async function applyEmailRuntimeConfigFromDb() {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
export async function readEmailRuntimeConfigFromDb() {
|
|
112
|
-
const raw = await
|
|
112
|
+
const raw = await getServerAuthorityStore()
|
|
113
|
+
.get(EMAIL_RUNTIME_CONFIG_KEY)
|
|
114
|
+
.catch(() => null);
|
|
113
115
|
if (!isRecord(raw)) {
|
|
114
116
|
return null;
|
|
115
117
|
}
|