nolo-cli 0.1.29 → 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/agentCreateCustomCommands.ts +9 -4
- package/agentDialogsCommands.ts +4 -3
- package/agentDoctorCommands.ts +3 -3
- package/agentListCommands.ts +3 -3
- package/agentMachineCommands.ts +6 -6
- package/agentPullCommand.ts +4 -12
- package/agentRecordCommands.ts +5 -3
- package/agentRecordHelpers.ts +42 -23
- package/agentRunCommand.ts +2 -6
- package/agentRunParsing.ts +18 -5
- package/agentRuntimeLocal.ts +10 -0
- package/agentUnpublishCommands.ts +3 -2
- 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 +68 -13
- 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 +40 -15
- 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/dialogReadCommands.ts +2 -1
- package/dialogStatusCommands.ts +2 -1
- 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 -62
- package/machineStatusCommands.ts +4 -12
- package/machineWsRunDispatch.ts +80 -15
- package/machineWsSession.ts +46 -2
- package/noloReadBridgePrompt.ts +16 -0
- package/package.json +114 -21
- package/pageCreateCommands.ts +2 -2
- package/pageDeleteCommands.ts +3 -3
- package/pageReadCommands.ts +3 -3
- package/pageUpdateCommands.ts +3 -3
- 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 +18 -69
- 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/spaceCategoryCommands.ts +6 -6
- package/spaceCommands.ts +3 -3
- package/spaceDeleteCommands.ts +2 -2
- package/spaceInternalCommandEntries.ts +2 -0
- package/spaceReadCommands.ts +3 -3
- package/tableMetaCommands.ts +2 -2
- package/taskRunCommand.ts +11 -0
- package/ai/agent/runtimeServerBase.ts +0 -37
- package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { authenticateRequest, CORS_HEADERS_AUTH } from "../../auth/utils";
|
|
2
2
|
import { DataType } from "../../create/types";
|
|
3
3
|
import { createKey, createTokenKey } from "../../database/keys";
|
|
4
|
-
import
|
|
4
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
5
5
|
|
|
6
6
|
const json = (data: any, status = 200) =>
|
|
7
7
|
new Response(JSON.stringify(data), {
|
|
@@ -32,6 +32,7 @@ const toNumber = (value: unknown) =>
|
|
|
32
32
|
typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
33
33
|
|
|
34
34
|
export const handleGetDialogBilling = async (req: any) => {
|
|
35
|
+
const store = getServerAuthorityStore();
|
|
35
36
|
const authResult = await authenticateRequest(req);
|
|
36
37
|
if (authResult instanceof Response) return authResult;
|
|
37
38
|
|
|
@@ -54,7 +55,7 @@ export const handleGetDialogBilling = async (req: any) => {
|
|
|
54
55
|
|
|
55
56
|
let dialog: any;
|
|
56
57
|
try {
|
|
57
|
-
dialog = await
|
|
58
|
+
dialog = await store.get(dialogKey);
|
|
58
59
|
} catch {
|
|
59
60
|
return json(
|
|
60
61
|
{
|
|
@@ -78,7 +79,7 @@ export const handleGetDialogBilling = async (req: any) => {
|
|
|
78
79
|
const { start, end } = createTokenKey.rangeOfUser(userId);
|
|
79
80
|
const records: BillingRecord[] = [];
|
|
80
81
|
|
|
81
|
-
for await (const [, value] of
|
|
82
|
+
for await (const [, value] of store.iterator({
|
|
82
83
|
gte: start,
|
|
83
84
|
lte: end,
|
|
84
85
|
reverse: true,
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import { authenticateRequest } from "../../auth/utils";
|
|
4
4
|
import { createTransactionKey } from "../../database/keys";
|
|
5
|
-
import
|
|
5
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
6
6
|
import pino from "pino";
|
|
7
7
|
|
|
8
8
|
const logger = pino({ name: "getTransactions" });
|
|
9
9
|
|
|
10
|
+
const getTransactionsStore = () => getServerAuthorityStore();
|
|
11
|
+
|
|
10
12
|
// 定义通用的 CORS 头
|
|
11
13
|
const CORS_HEADERS = {
|
|
12
14
|
"Access-Control-Allow-Origin": "*",
|
|
@@ -36,6 +38,7 @@ const createJsonResponse = (data: any, status: number = 200): Response => {
|
|
|
36
38
|
*/
|
|
37
39
|
export const handleGetTransactions = async (req: Request) => {
|
|
38
40
|
try {
|
|
41
|
+
const store = getTransactionsStore();
|
|
39
42
|
// 步骤 1: 验证用户身份
|
|
40
43
|
const authResult = await authenticateRequest(req);
|
|
41
44
|
if (authResult instanceof Response) {
|
|
@@ -73,7 +76,7 @@ export const handleGetTransactions = async (req: Request) => {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
// 步骤 4: 查询数据库
|
|
76
|
-
const iterator =
|
|
79
|
+
const iterator = store.iterator(queryOptions);
|
|
77
80
|
const transactions = [];
|
|
78
81
|
for await (const [, value] of iterator) {
|
|
79
82
|
transactions.push(value);
|
|
@@ -34,14 +34,6 @@ type MachineHandlerDeps = {
|
|
|
34
34
|
|
|
35
35
|
type SmokeCliProvider = "codex" | "copilot" | "claude" | "agy" | "gemini";
|
|
36
36
|
|
|
37
|
-
const SMOKE_CAPABILITY_BY_PROVIDER: Record<SmokeCliProvider, string> = {
|
|
38
|
-
codex: "codex-cli",
|
|
39
|
-
copilot: "copilot-cli",
|
|
40
|
-
claude: "claude-code",
|
|
41
|
-
agy: "agy-cli",
|
|
42
|
-
gemini: "gemini-cli",
|
|
43
|
-
};
|
|
44
|
-
|
|
45
37
|
function normalizeSmokeProvider(value: unknown): SmokeCliProvider | null {
|
|
46
38
|
if (
|
|
47
39
|
value === "codex" ||
|
|
@@ -230,12 +222,6 @@ export function createMachineHandlers(deps: MachineHandlerDeps = {}) {
|
|
|
230
222
|
if (!connectorRegistry.hasConnection(auth.userId, machineId)) {
|
|
231
223
|
return json({ error: "Machine connector is not connected" }, 409);
|
|
232
224
|
}
|
|
233
|
-
|
|
234
|
-
const requiredCapability = SMOKE_CAPABILITY_BY_PROVIDER[cliProvider];
|
|
235
|
-
if (!machine.capabilities.includes(requiredCapability)) {
|
|
236
|
-
return json({ error: `Machine is missing capability: ${requiredCapability}` }, 409);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
225
|
try {
|
|
240
226
|
const result = await connectorRegistry.sendAgentRun(auth.userId, machineId, {
|
|
241
227
|
agentKey: `machine-smoke-${cliProvider}`,
|
|
@@ -331,13 +331,15 @@ function runCopilot(prompt, options) {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
function runClaude(prompt, options) {
|
|
334
|
-
const args = ["-p", prompt, "--permission-mode", "bypassPermissions"];
|
|
335
|
-
if (options.model) args.push("--model", options.model);
|
|
334
|
+
const args = ["--add-dir", process.cwd(), "-p", prompt, "--permission-mode", "bypassPermissions"];
|
|
335
|
+
if (options.model && process.env.NOLO_CLAUDE_CLI_ALLOW_MODEL === "1") args.push("--model", options.model);
|
|
336
336
|
return runProcess("claude", args, options);
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
function runAgy(prompt, options) {
|
|
340
340
|
return runProcess("agy", [
|
|
341
|
+
"--add-dir",
|
|
342
|
+
process.cwd(),
|
|
341
343
|
"--print",
|
|
342
344
|
prompt,
|
|
343
345
|
"--print-timeout",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getServerAuthorityStore } from "../../database/server/db";
|
|
2
2
|
import { parseSpaceMemberKey } from "../../database/server/spaceMemberAuthority";
|
|
3
3
|
import {
|
|
4
4
|
type NotificationRecord,
|
|
@@ -69,14 +69,16 @@ const mergePreservedFields = (
|
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
const getNotificationStore = () => getServerAuthorityStore();
|
|
73
|
+
|
|
72
74
|
const upsertNotificationRecord = async (
|
|
73
75
|
notification: NotificationRecord
|
|
74
76
|
): Promise<NotificationRecord> => {
|
|
75
|
-
const existing = (await
|
|
77
|
+
const existing = (await getNotificationStore()
|
|
76
78
|
.get(notification.dbKey)
|
|
77
79
|
.catch(() => null)) as NotificationRecord | null;
|
|
78
80
|
const merged = mergePreservedFields(notification, existing);
|
|
79
|
-
await
|
|
81
|
+
await getNotificationStore().put(merged.dbKey, merged);
|
|
80
82
|
pushEvent(`user-${merged.userId}`, {
|
|
81
83
|
type: "notification.upsert",
|
|
82
84
|
notification: merged,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { authenticateRequest, CORS_HEADERS_AUTH } from "../../auth/utils";
|
|
2
2
|
import { createNotificationKey } from "../../database/keys";
|
|
3
|
-
import
|
|
3
|
+
import { ensureServerDbOpen, getServerAuthorityStore } from "../../database/server/db";
|
|
4
4
|
import type { NotificationRecord } from "../../app/notifications/model";
|
|
5
5
|
|
|
6
6
|
const JSON_HEADERS = {
|
|
@@ -27,6 +27,8 @@ const isLevelLifecycleError = (error: unknown) => {
|
|
|
27
27
|
return code === "LEVEL_DATABASE_NOT_OPEN" || code === "LEVEL_ITERATOR_NOT_OPEN";
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
const getNotificationsStore = () => getServerAuthorityStore();
|
|
31
|
+
|
|
30
32
|
export const handleNotifications = async (req: Request): Promise<Response> => {
|
|
31
33
|
if (req.method === "OPTIONS") {
|
|
32
34
|
return new Response(null, { status: 204, headers: CORS_HEADERS_AUTH });
|
|
@@ -57,9 +59,10 @@ export const handleNotifications = async (req: Request): Promise<Response> => {
|
|
|
57
59
|
|
|
58
60
|
const { start, end } = createNotificationKey.rangeOfUser(auth.userId);
|
|
59
61
|
const notifications: NotificationRecord[] = [];
|
|
62
|
+
const store = getNotificationsStore();
|
|
60
63
|
|
|
61
64
|
try {
|
|
62
|
-
for await (const [key, value] of
|
|
65
|
+
for await (const [key, value] of store.iterator({ gte: start, lte: end })) {
|
|
63
66
|
const record = value as NotificationRecord | null;
|
|
64
67
|
if (!record || typeof record !== "object") continue;
|
|
65
68
|
if (record.deletedAt) continue;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import type { ProviderCallEvent } from "../../ai/token/providerCall";
|
|
2
|
+
import type { ProviderDispatchIntent } from "../../ai/token/providerDispatchIntent";
|
|
3
|
+
|
|
4
|
+
const JSON_HEADERS = {
|
|
5
|
+
"content-type": "application/json; charset=utf-8",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type ProviderCallsRecentEnv = NodeJS.ProcessEnv & {
|
|
9
|
+
BILLING_LEDGER_AUDIT_SECRET?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type ProviderCallsRecentStore = {
|
|
13
|
+
iterator(options: { gte?: string; lte?: string }): AsyncIterable<[string, unknown]>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const PROVIDER_CALL_PREFIX = "provider-call-";
|
|
17
|
+
const PROVIDER_CALL_RANGE_END = `${PROVIDER_CALL_PREFIX}\uffff`;
|
|
18
|
+
const PROVIDER_DISPATCH_INTENT_PREFIX = "provider-dispatch-intent-";
|
|
19
|
+
const PROVIDER_DISPATCH_INTENT_RANGE_END = `${PROVIDER_DISPATCH_INTENT_PREFIX}\uffff`;
|
|
20
|
+
|
|
21
|
+
const json = (body: unknown, status = 200) =>
|
|
22
|
+
new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
|
|
23
|
+
|
|
24
|
+
const parseOptionalTimestamp = (value: string | null) => {
|
|
25
|
+
if (!value) return null;
|
|
26
|
+
const trimmed = value.trim();
|
|
27
|
+
if (!trimmed) return null;
|
|
28
|
+
if (/^\d+$/.test(trimmed)) return Number(trimmed);
|
|
29
|
+
const parsed = Date.parse(trimmed);
|
|
30
|
+
if (!Number.isFinite(parsed)) {
|
|
31
|
+
throw new Error(`invalid timestamp: ${value}`);
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const parseLimit = (value: string | null) => {
|
|
37
|
+
const parsed = Number(value);
|
|
38
|
+
if (!Number.isFinite(parsed)) return 50;
|
|
39
|
+
return Math.min(Math.max(Math.floor(parsed), 1), 500);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const eventTimestamp = (event: ProviderCallEvent) => {
|
|
43
|
+
if (event.status === "completed") return Date.parse(event.completedAt);
|
|
44
|
+
if (event.status === "failed") return Date.parse(event.failedAt);
|
|
45
|
+
return Date.parse(event.startedAt);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const isProviderCallEvent = (value: unknown): value is ProviderCallEvent =>
|
|
49
|
+
Boolean(
|
|
50
|
+
value &&
|
|
51
|
+
typeof value === "object" &&
|
|
52
|
+
typeof (value as { providerCallId?: unknown }).providerCallId === "string" &&
|
|
53
|
+
typeof (value as { eventId?: unknown }).eventId === "string" &&
|
|
54
|
+
typeof (value as { status?: unknown }).status === "string"
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const isProviderDispatchIntent = (value: unknown): value is ProviderDispatchIntent =>
|
|
58
|
+
Boolean(
|
|
59
|
+
value &&
|
|
60
|
+
typeof value === "object" &&
|
|
61
|
+
typeof (value as { providerCallId?: unknown }).providerCallId === "string" &&
|
|
62
|
+
typeof (value as { intentId?: unknown }).intentId === "string" &&
|
|
63
|
+
(value as { status?: unknown }).status === "dispatching"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const resolveDefaultStore = async (): Promise<ProviderCallsRecentStore> => {
|
|
67
|
+
const dbModule = await import("../../database/server/db");
|
|
68
|
+
return dbModule.default as ProviderCallsRecentStore;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export async function buildProviderCallsRecentReport({
|
|
72
|
+
store,
|
|
73
|
+
sinceMs,
|
|
74
|
+
untilMs,
|
|
75
|
+
limit,
|
|
76
|
+
dialogId,
|
|
77
|
+
provider,
|
|
78
|
+
model,
|
|
79
|
+
}: {
|
|
80
|
+
store: ProviderCallsRecentStore;
|
|
81
|
+
sinceMs: number;
|
|
82
|
+
untilMs: number;
|
|
83
|
+
limit: number;
|
|
84
|
+
dialogId?: string;
|
|
85
|
+
provider?: string;
|
|
86
|
+
model?: string;
|
|
87
|
+
}) {
|
|
88
|
+
const byCall = new Map<
|
|
89
|
+
string,
|
|
90
|
+
{
|
|
91
|
+
events: ProviderCallEvent[];
|
|
92
|
+
dispatchIntents: ProviderDispatchIntent[];
|
|
93
|
+
}
|
|
94
|
+
>();
|
|
95
|
+
|
|
96
|
+
for await (const [, value] of store.iterator({
|
|
97
|
+
gte: PROVIDER_CALL_PREFIX,
|
|
98
|
+
lte: PROVIDER_CALL_RANGE_END,
|
|
99
|
+
})) {
|
|
100
|
+
if (!isProviderCallEvent(value)) continue;
|
|
101
|
+
const timestamp = eventTimestamp(value);
|
|
102
|
+
if (!Number.isFinite(timestamp) || timestamp < sinceMs || timestamp > untilMs) continue;
|
|
103
|
+
if (dialogId && value.dialogId !== dialogId) continue;
|
|
104
|
+
if (provider && value.provider !== provider) continue;
|
|
105
|
+
if (model && value.model !== model) continue;
|
|
106
|
+
const existing = byCall.get(value.providerCallId) ?? {
|
|
107
|
+
events: [],
|
|
108
|
+
dispatchIntents: [],
|
|
109
|
+
};
|
|
110
|
+
existing.events.push(value);
|
|
111
|
+
byCall.set(value.providerCallId, existing);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for await (const [, value] of store.iterator({
|
|
115
|
+
gte: PROVIDER_DISPATCH_INTENT_PREFIX,
|
|
116
|
+
lte: PROVIDER_DISPATCH_INTENT_RANGE_END,
|
|
117
|
+
})) {
|
|
118
|
+
if (!isProviderDispatchIntent(value)) continue;
|
|
119
|
+
const existing = byCall.get(value.providerCallId);
|
|
120
|
+
if (!existing) continue;
|
|
121
|
+
existing.dispatchIntents.push(value);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const rows = Array.from(byCall.entries())
|
|
125
|
+
.map(([providerCallId, state]) => {
|
|
126
|
+
const sortedEvents = [...state.events].sort(
|
|
127
|
+
(a, b) => eventTimestamp(a) - eventTimestamp(b)
|
|
128
|
+
);
|
|
129
|
+
const first = sortedEvents[0];
|
|
130
|
+
const last = sortedEvents[sortedEvents.length - 1];
|
|
131
|
+
return {
|
|
132
|
+
providerCallId,
|
|
133
|
+
userId: first.userId,
|
|
134
|
+
dialogId: first.dialogId,
|
|
135
|
+
agentId: first.agentId,
|
|
136
|
+
provider: first.provider,
|
|
137
|
+
model: first.model,
|
|
138
|
+
endpoint: first.endpoint,
|
|
139
|
+
serviceTier: first.serviceTier,
|
|
140
|
+
credential: first.credential,
|
|
141
|
+
startedAt: first.startedAt,
|
|
142
|
+
lastEventAt: new Date(eventTimestamp(last)).toISOString(),
|
|
143
|
+
statuses: sortedEvents.map((event) => event.status),
|
|
144
|
+
billingStatus:
|
|
145
|
+
last.status === "completed" || last.status === "failed"
|
|
146
|
+
? last.billingStatus
|
|
147
|
+
: undefined,
|
|
148
|
+
events: sortedEvents.map((event) => ({
|
|
149
|
+
eventId: event.eventId,
|
|
150
|
+
status: event.status,
|
|
151
|
+
timestamp: new Date(eventTimestamp(event)).toISOString(),
|
|
152
|
+
})),
|
|
153
|
+
dispatchIntents: state.dispatchIntents
|
|
154
|
+
.sort((a, b) => Date.parse(a.createdAt) - Date.parse(b.createdAt))
|
|
155
|
+
.map((intent) => ({
|
|
156
|
+
intentId: intent.intentId,
|
|
157
|
+
createdAt: intent.createdAt,
|
|
158
|
+
credential: intent.credential,
|
|
159
|
+
request: {
|
|
160
|
+
url: intent.request.url,
|
|
161
|
+
method: intent.request.method,
|
|
162
|
+
bodyHash: intent.request.bodyHash,
|
|
163
|
+
},
|
|
164
|
+
})),
|
|
165
|
+
};
|
|
166
|
+
})
|
|
167
|
+
.sort((a, b) => Date.parse(b.lastEventAt) - Date.parse(a.lastEventAt))
|
|
168
|
+
.slice(0, limit);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
sinceMs,
|
|
172
|
+
untilMs,
|
|
173
|
+
summary: {
|
|
174
|
+
providerCallCount: rows.length,
|
|
175
|
+
eventCount: rows.reduce((sum, row) => sum + row.events.length, 0),
|
|
176
|
+
dispatchIntentCount: rows.reduce(
|
|
177
|
+
(sum, row) => sum + row.dispatchIntents.length,
|
|
178
|
+
0
|
|
179
|
+
),
|
|
180
|
+
},
|
|
181
|
+
providerCalls: rows,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function createProviderCallsRecentHandler({
|
|
186
|
+
store,
|
|
187
|
+
env = process.env,
|
|
188
|
+
nowMs,
|
|
189
|
+
}: {
|
|
190
|
+
store?: ProviderCallsRecentStore;
|
|
191
|
+
env?: Partial<ProviderCallsRecentEnv>;
|
|
192
|
+
nowMs?: number;
|
|
193
|
+
} = {}) {
|
|
194
|
+
return async function handleProviderCallsRecent(req: Request) {
|
|
195
|
+
const expectedSecret = String(env.BILLING_LEDGER_AUDIT_SECRET || "").trim();
|
|
196
|
+
if (!expectedSecret) {
|
|
197
|
+
return new Response("Not Found", { status: 404 });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const providedSecret =
|
|
201
|
+
req.headers.get("x-billing-ledger-audit-secret")?.trim() || "";
|
|
202
|
+
if (providedSecret !== expectedSecret) {
|
|
203
|
+
return json({ success: false, error: "Unauthorized" }, 401);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const url = new URL(req.url);
|
|
208
|
+
const now = nowMs ?? Date.now();
|
|
209
|
+
const untilMs = parseOptionalTimestamp(url.searchParams.get("until")) ?? now;
|
|
210
|
+
const sinceMs =
|
|
211
|
+
parseOptionalTimestamp(url.searchParams.get("since")) ?? untilMs - 3600_000;
|
|
212
|
+
const report = await buildProviderCallsRecentReport({
|
|
213
|
+
store: store ?? (await resolveDefaultStore()),
|
|
214
|
+
sinceMs,
|
|
215
|
+
untilMs,
|
|
216
|
+
limit: parseLimit(url.searchParams.get("limit")),
|
|
217
|
+
dialogId: url.searchParams.get("dialogId")?.trim() || undefined,
|
|
218
|
+
provider: url.searchParams.get("provider")?.trim() || undefined,
|
|
219
|
+
model: url.searchParams.get("model")?.trim() || undefined,
|
|
220
|
+
});
|
|
221
|
+
return json({ success: true, report });
|
|
222
|
+
} catch (error) {
|
|
223
|
+
return json(
|
|
224
|
+
{
|
|
225
|
+
success: false,
|
|
226
|
+
error: error instanceof Error ? error.message : String(error),
|
|
227
|
+
},
|
|
228
|
+
400
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const handleProviderCallsRecent = createProviderCallsRecentHandler();
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
createBillingAdminAuditEvent,
|
|
4
|
+
type BillingAdminAuditAction,
|
|
5
|
+
} from "../../ai/token/billingAdminAuditEvent";
|
|
6
|
+
import { writeBillingAdminAuditEvent } from "../../ai/token/billingAdminAuditEventWriter";
|
|
7
|
+
import type { NormalizedOfficialProviderBucket } from "../../ai/token/deepinfraReconciliation";
|
|
8
|
+
import { runProviderReconciliation } from "../../ai/token/providerReconciliationRunner";
|
|
9
|
+
import { ulid } from "ulid";
|
|
10
|
+
|
|
11
|
+
const JSON_HEADERS = {
|
|
12
|
+
"content-type": "application/json; charset=utf-8",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type ProviderReconciliationRunEnv = NodeJS.ProcessEnv & {
|
|
16
|
+
BILLING_LEDGER_AUDIT_SECRET?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type ProviderReconciliationRunStore = Parameters<typeof runProviderReconciliation>[0]["store"];
|
|
20
|
+
|
|
21
|
+
const json = (body: unknown, status = 200) =>
|
|
22
|
+
new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
|
|
23
|
+
|
|
24
|
+
const resolveDefaultStore = async (): Promise<ProviderReconciliationRunStore> => {
|
|
25
|
+
const dbModule = await import("../../database/server/db");
|
|
26
|
+
return dbModule.default as ProviderReconciliationRunStore;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const ALLOWED_PROVIDERS = new Set(["deepinfra", "openai"]);
|
|
30
|
+
|
|
31
|
+
const stableStringify = (value: unknown): string => {
|
|
32
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
33
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
34
|
+
const record = value as Record<string, unknown>;
|
|
35
|
+
return `{${Object.keys(record)
|
|
36
|
+
.sort()
|
|
37
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
|
|
38
|
+
.join(",")}}`;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const buildInputHash = (officialBuckets: NormalizedOfficialProviderBucket[]) =>
|
|
42
|
+
`sha256:${createHash("sha256")
|
|
43
|
+
.update(stableStringify(officialBuckets))
|
|
44
|
+
.digest("hex")}`;
|
|
45
|
+
|
|
46
|
+
const isOfficialBucket = (value: unknown): value is NormalizedOfficialProviderBucket => {
|
|
47
|
+
if (!value || typeof value !== "object") return false;
|
|
48
|
+
const bucket = value as NormalizedOfficialProviderBucket;
|
|
49
|
+
return (
|
|
50
|
+
typeof bucket.provider === "string" &&
|
|
51
|
+
typeof bucket.bucketStart === "string" &&
|
|
52
|
+
typeof bucket.bucketEnd === "string" &&
|
|
53
|
+
Boolean(bucket.official) &&
|
|
54
|
+
typeof bucket.official.rawProviderCostUsd === "number" &&
|
|
55
|
+
typeof bucket.official.inputTokens === "number" &&
|
|
56
|
+
typeof bucket.official.outputTokens === "number" &&
|
|
57
|
+
typeof bucket.official.requestCount === "number"
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function createProviderReconciliationRunHandler({
|
|
62
|
+
store,
|
|
63
|
+
env = process.env,
|
|
64
|
+
now = () => new Date().toISOString(),
|
|
65
|
+
createEventId = () => `evt_${ulid()}`,
|
|
66
|
+
}: {
|
|
67
|
+
store?: ProviderReconciliationRunStore;
|
|
68
|
+
env?: Partial<ProviderReconciliationRunEnv>;
|
|
69
|
+
now?: () => string;
|
|
70
|
+
createEventId?: () => string;
|
|
71
|
+
} = {}) {
|
|
72
|
+
return async function handleProviderReconciliationRun(req: Request) {
|
|
73
|
+
const expectedSecret = String(env.BILLING_LEDGER_AUDIT_SECRET || "").trim();
|
|
74
|
+
if (!expectedSecret) {
|
|
75
|
+
return new Response("Not Found", { status: 404 });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const providedSecret =
|
|
79
|
+
req.headers.get("x-billing-ledger-audit-secret")?.trim() || "";
|
|
80
|
+
if (providedSecret !== expectedSecret) {
|
|
81
|
+
return json({ success: false, error: "Unauthorized" }, 401);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (req.method !== "POST") {
|
|
85
|
+
return json({ success: false, error: "Method Not Allowed" }, 405);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const payload = await req.json();
|
|
90
|
+
const officialBuckets = Array.isArray(payload?.officialBuckets)
|
|
91
|
+
? payload.officialBuckets
|
|
92
|
+
: [];
|
|
93
|
+
if (officialBuckets.length === 0) {
|
|
94
|
+
throw new Error("officialBuckets must be a non-empty array");
|
|
95
|
+
}
|
|
96
|
+
if (!officialBuckets.every(isOfficialBucket)) {
|
|
97
|
+
throw new Error("officialBuckets contains invalid bucket records");
|
|
98
|
+
}
|
|
99
|
+
const provider = typeof payload.provider === "string" ? payload.provider.trim() : "";
|
|
100
|
+
if (!provider || !ALLOWED_PROVIDERS.has(provider)) {
|
|
101
|
+
throw new Error("provider must be one of: deepinfra, openai");
|
|
102
|
+
}
|
|
103
|
+
if (!officialBuckets.every((bucket) => bucket.provider === provider)) {
|
|
104
|
+
throw new Error("officialBuckets provider must match provider");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const inputHash = buildInputHash(officialBuckets);
|
|
108
|
+
const targetStore = store ?? (await resolveDefaultStore());
|
|
109
|
+
const dryRun = payload.dryRun === true;
|
|
110
|
+
const createdAt = typeof payload.createdAt === "string" ? payload.createdAt : now();
|
|
111
|
+
const preview = await runProviderReconciliation({
|
|
112
|
+
store: targetStore,
|
|
113
|
+
officialBuckets,
|
|
114
|
+
createdAt,
|
|
115
|
+
dryRun: true,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!dryRun) {
|
|
119
|
+
if (payload.confirmCommit !== true) {
|
|
120
|
+
throw new Error("confirmCommit must be true for non-dry-run reconciliation");
|
|
121
|
+
}
|
|
122
|
+
if (payload.inputHash !== inputHash) {
|
|
123
|
+
throw new Error("inputHash must match the officialBuckets hash");
|
|
124
|
+
}
|
|
125
|
+
const maxRiskCredits = Number(payload.maxRiskCredits);
|
|
126
|
+
if (!Number.isFinite(maxRiskCredits) || maxRiskCredits < 0) {
|
|
127
|
+
throw new Error("maxRiskCredits must be a non-negative number");
|
|
128
|
+
}
|
|
129
|
+
if (preview.riskPlatformCreditsProposed > maxRiskCredits) {
|
|
130
|
+
throw new Error("maxRiskCredits is below projected reconciliation risk");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const result = dryRun
|
|
135
|
+
? preview
|
|
136
|
+
: await runProviderReconciliation({
|
|
137
|
+
store: targetStore,
|
|
138
|
+
officialBuckets,
|
|
139
|
+
createdAt,
|
|
140
|
+
dryRun: false,
|
|
141
|
+
});
|
|
142
|
+
const action: BillingAdminAuditAction = dryRun
|
|
143
|
+
? "reconciliation_dry_run"
|
|
144
|
+
: "reconciliation_committed";
|
|
145
|
+
await writeBillingAdminAuditEvent({
|
|
146
|
+
store: targetStore,
|
|
147
|
+
event: createBillingAdminAuditEvent({
|
|
148
|
+
eventId: createEventId(),
|
|
149
|
+
actorId:
|
|
150
|
+
typeof payload.actorId === "string" && payload.actorId.trim()
|
|
151
|
+
? payload.actorId
|
|
152
|
+
: "billing-admin-api",
|
|
153
|
+
action,
|
|
154
|
+
anomalyId: "provider-reconciliation",
|
|
155
|
+
planId: `provider-reconciliation:${provider}`,
|
|
156
|
+
inputSetHash: inputHash,
|
|
157
|
+
createdAt,
|
|
158
|
+
reason: JSON.stringify({
|
|
159
|
+
provider,
|
|
160
|
+
dryRun,
|
|
161
|
+
bucketCount: officialBuckets.length,
|
|
162
|
+
riskPlatformCreditsProposed: preview.riskPlatformCreditsProposed,
|
|
163
|
+
}),
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return json({ success: true, inputHash, result });
|
|
168
|
+
} catch (error) {
|
|
169
|
+
return json(
|
|
170
|
+
{
|
|
171
|
+
success: false,
|
|
172
|
+
error: error instanceof Error ? error.message : String(error),
|
|
173
|
+
},
|
|
174
|
+
400
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export const handleProviderReconciliationRun = createProviderReconciliationRunHandler();
|