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.
Files changed (308) hide show
  1. package/agent-runtime/agentRecordConfig.ts +0 -2
  2. package/agent-runtime/hostAdapter.ts +0 -1
  3. package/agent-runtime/hostRuntimeCapabilities.ts +7 -0
  4. package/agent-runtime/hostRuntimeConfig.ts +86 -0
  5. package/agent-runtime/index.ts +13 -0
  6. package/agent-runtime/localToolPolicy.ts +13 -6
  7. package/agent-runtime/localWorkspaceTools.ts +333 -30
  8. package/agent-runtime/platformChatProvider.ts +4 -1
  9. package/agent-runtime/runtimeToolPolicy.ts +32 -47
  10. package/agent-runtime/shellCommandPolicy.ts +117 -0
  11. package/agent-runtime/toolGate.ts +38 -0
  12. package/agentRecordCommands.ts +1 -1
  13. package/agentRecordHelpers.ts +35 -20
  14. package/agentRunParsing.ts +18 -5
  15. package/agentRuntimeLocal.ts +10 -0
  16. package/ai/agent/agentRunDialogLineage.ts +56 -0
  17. package/ai/agent/agentSlice.ts +45 -10
  18. package/ai/agent/avatarUtils.ts +1 -1
  19. package/ai/agent/buildSystemPrompt.ts +1 -2
  20. package/ai/agent/cliExecutor.ts +330 -19
  21. package/ai/agent/createAgentSchema.ts +22 -0
  22. package/ai/agent/hooks/fetchPublicAgents.ts +14 -39
  23. package/ai/agent/hooks/usePublicAgents.ts +90 -192
  24. package/ai/agent/publicAgentCatalog.ts +359 -0
  25. package/ai/agent/publicAgentIdentity.ts +76 -0
  26. package/ai/agent/server/fetchPublicAgents.ts +21 -46
  27. package/ai/agent/serverOrigin.ts +7 -0
  28. package/ai/agent/streamAgentChatTurn.ts +23 -21
  29. package/ai/agent/streamAgentChatTurnUtils.ts +292 -6
  30. package/ai/agent/types.ts +2 -0
  31. package/ai/agent/web/agentDisplayUtils.ts +171 -0
  32. package/ai/agent/web/useAgentModelAvatarComponent.ts +32 -0
  33. package/ai/ai.locale.ts +8 -0
  34. package/ai/chat/sendOpenAICompletionsRequest.native.ts +15 -0
  35. package/ai/chat/sendOpenAICompletionsRequest.ts +93 -1
  36. package/ai/chat/sendOpenAIResponseRequest.ts +86 -9
  37. package/ai/chat/updateTotalUsage.ts +7 -0
  38. package/ai/llm/deepinfra.ts +30 -15
  39. package/ai/llm/mistral.ts +3 -4
  40. package/ai/llm/providers.ts +2 -0
  41. package/ai/llm/types.ts +15 -0
  42. package/ai/memory/capture.ts +3 -3
  43. package/ai/memory/consolidate.ts +2 -2
  44. package/ai/memory/delete.ts +4 -3
  45. package/ai/memory/query.ts +3 -3
  46. package/ai/memory/remember.ts +2 -2
  47. package/ai/memory/store.ts +3 -3
  48. package/ai/memory/storeShared.ts +3 -2
  49. package/ai/policy/commercialMultiplier.ts +16 -0
  50. package/ai/token/billingAdminAuditEvent.ts +68 -0
  51. package/ai/token/billingAdminAuditEventWriter.ts +41 -0
  52. package/ai/token/billingAnomaly.ts +90 -0
  53. package/ai/token/billingAnomalyLifecycle.ts +54 -0
  54. package/ai/token/billingAnomalyLifecycleWriter.ts +44 -0
  55. package/ai/token/billingAnomalyWriter.ts +41 -0
  56. package/ai/token/calculatePrice.ts +3 -2
  57. package/ai/token/deepinfraReconciliation.ts +153 -0
  58. package/ai/token/deepinfraRequestIdQuery.ts +92 -0
  59. package/ai/token/externalToolCost.ts +54 -41
  60. package/ai/token/missingUsageEstimate.ts +12 -3
  61. package/ai/token/normalizeUsage.ts +45 -5
  62. package/ai/token/openaiReconciliation.ts +188 -0
  63. package/ai/token/providerBillableEvent.ts +84 -0
  64. package/ai/token/providerBillableEventWriter.ts +36 -0
  65. package/ai/token/providerBillingAnomalyDrilldown.ts +308 -0
  66. package/ai/token/providerBillingDrilldown.ts +361 -0
  67. package/ai/token/providerBillingHealthReport.ts +366 -0
  68. package/ai/token/providerCall.ts +144 -0
  69. package/ai/token/providerCallSweeper.ts +168 -0
  70. package/ai/token/providerCallWriter.ts +33 -0
  71. package/ai/token/providerCredential.ts +89 -0
  72. package/ai/token/providerCredentialAnomaly.ts +154 -0
  73. package/ai/token/providerCredentialLifecycle.ts +59 -0
  74. package/ai/token/providerCredentialLifecycleWriter.ts +44 -0
  75. package/ai/token/providerCredentialRegistry.ts +83 -0
  76. package/ai/token/providerCredentialRegistryCommand.ts +97 -0
  77. package/ai/token/providerCredentialRegistryList.ts +124 -0
  78. package/ai/token/providerCredentialRegistryResolver.ts +150 -0
  79. package/ai/token/providerCredentialRegistryWriter.ts +41 -0
  80. package/ai/token/providerCredentialRotationCommand.ts +102 -0
  81. package/ai/token/providerCredentialRuntime.ts +56 -0
  82. package/ai/token/providerDispatchIntent.ts +67 -0
  83. package/ai/token/providerDispatchIntentWriter.ts +36 -0
  84. package/ai/token/providerGateway.ts +260 -0
  85. package/ai/token/providerReconciliation.ts +148 -0
  86. package/ai/token/providerReconciliationLocal.ts +97 -0
  87. package/ai/token/providerReconciliationRunner.ts +165 -0
  88. package/ai/token/providerReconciliationWriter.ts +41 -0
  89. package/ai/token/ratingResult.ts +202 -0
  90. package/ai/token/ratingResultWriter.ts +33 -0
  91. package/ai/token/serverDialogProjection.ts +5 -3
  92. package/ai/token/serverTokenWriter.ts +11 -3
  93. package/ai/token/types.ts +10 -0
  94. package/ai/tools/agent/callAgentTool.ts +3 -4
  95. package/ai/tools/agent/presets/appBuilderPreset.ts +5 -7
  96. package/ai/tools/agent/runStreamingAgentTool.ts +1 -2
  97. package/ai/tools/agent/streamParallelAgentsTool.ts +2 -3
  98. package/ai/tools/readXPostTool.ts +1 -5
  99. package/app/admin/adminPages.ts +9 -0
  100. package/app/i18n/i18n.config.ts +5 -0
  101. package/app/i18n/translations/pricing.locale.ts +397 -0
  102. package/app/utils/desktopAgentRuntimeStatusClient.ts +5 -4
  103. package/app/utils/desktopAgentRuntimeTurnClient.ts +4 -1
  104. package/app/utils/imageUtils.ts +64 -34
  105. package/auth/adminPermissions.ts +18 -0
  106. package/auth/authSlice.ts +26 -12
  107. package/auth/hooks/useFetchUsers.ts +3 -0
  108. package/auth/routes.ts +46 -0
  109. package/auth/server/accessActivity.ts +4 -3
  110. package/auth/server/adminPermissions.ts +43 -0
  111. package/auth/server/adminUsageReport.ts +375 -0
  112. package/auth/server/billingAnomalies.ts +235 -0
  113. package/auth/server/billingUsageReport.ts +41 -2
  114. package/auth/server/creatorEarningCreditConversion.ts +10 -2
  115. package/auth/server/deduct.ts +14 -7
  116. package/auth/server/delete.ts +10 -2
  117. package/auth/server/disable.ts +4 -3
  118. package/auth/server/emailAutomation.ts +7 -5
  119. package/auth/server/emailDbConfig.ts +4 -2
  120. package/auth/server/emailDelivery.ts +24 -20
  121. package/auth/server/emailDeliveryGuards.ts +10 -7
  122. package/auth/server/emailPreferences.ts +6 -4
  123. package/auth/server/emailReport.ts +6 -4
  124. package/auth/server/emailRuntimeConfig.ts +3 -3
  125. package/auth/server/emailScenarioNotifications.ts +9 -8
  126. package/auth/server/enable.ts +4 -3
  127. package/auth/server/getRechargeHistory.ts +3 -2
  128. package/auth/server/getUser.ts +3 -2
  129. package/auth/server/gptProAccess.ts +44 -0
  130. package/auth/server/inviteReward.ts +10 -7
  131. package/auth/server/listusers.ts +6 -4
  132. package/auth/server/login.ts +2 -2
  133. package/auth/server/productEntitlements.ts +7 -4
  134. package/auth/server/productPurchase.ts +10 -2
  135. package/auth/server/providerBillingDrilldown.ts +47 -0
  136. package/auth/server/providerBillingHealth.ts +71 -0
  137. package/auth/server/providerCredentials.ts +136 -0
  138. package/auth/server/recharge.ts +14 -8
  139. package/auth/server/revokeSessions.ts +4 -3
  140. package/auth/server/route.ts +77 -0
  141. package/auth/server/sendEmail.ts +5 -4
  142. package/auth/server/signup.ts +10 -3
  143. package/auth/server/token.ts +53 -55
  144. package/auth/server/tokenUsageBilling.ts +18 -7
  145. package/auth/server/usageManagementAccess.ts +43 -0
  146. package/auth/server/usageReport.ts +59 -0
  147. package/auth/types.ts +12 -0
  148. package/auth/utils.ts +24 -22
  149. package/authCommands.ts +36 -2
  150. package/buildPublish.ts +3 -1
  151. package/buildPublishArtifactAssembly.ts +62 -1
  152. package/buildPublishImportAnalysis.ts +21 -16
  153. package/chat/dialog/dialogLaunchScope.ts +32 -0
  154. package/chat/dialog/objectAssistantRegistry.ts +2 -2
  155. package/chat/dialog/useCreateDialog.ts +1 -1
  156. package/chat/hooks/useChatInput.ts +41 -10
  157. package/chat/messages/messageSlice.ts +5 -1
  158. package/chat/messages/pendingAttachmentParts.ts +28 -2
  159. package/chat/messages/sendFirstMessage.ts +76 -79
  160. package/chat/messages/types.ts +9 -0
  161. package/chat/messages/web/streamingMarkdownModel.ts +41 -0
  162. package/chat/messages/web/useStreamingReveal.ts +91 -0
  163. package/chat/web/messageInputAgentUi.ts +94 -6
  164. package/cliEnvHelpers.ts +8 -0
  165. package/cliRuntimeDoctorReport.ts +193 -0
  166. package/client/agentConfigResolver.test.ts +0 -2
  167. package/client/agentRun.test.ts +171 -10
  168. package/client/agentRun.ts +44 -637
  169. package/client/agentRunInput.ts +10 -0
  170. package/client/agentRunLocalExecutor.ts +179 -0
  171. package/client/agentRunServerTransport.ts +394 -0
  172. package/client/agentRunTaskRunRecorder.ts +176 -0
  173. package/client/localRuntimeAdapter.test.ts +236 -9
  174. package/client/localRuntimeAdapter.ts +221 -23
  175. package/client/localToolPolicy.test.ts +28 -4
  176. package/connector-experimental/daemonLauncher.ts +38 -4
  177. package/connectorRunArtifact.ts +34 -10
  178. package/connectorWebSocketTarget.ts +9 -5
  179. package/core/builtinAgents.ts +155 -11
  180. package/create/space/contentLabels.ts +1 -1
  181. package/create/space/contentTypeMeta.ts +3 -2
  182. package/create/space/sidebarVisibleTypes.ts +27 -0
  183. package/create/space/space.locale.ts +76 -4
  184. package/database/actions/agentReadResolution.ts +2 -8
  185. package/database/actions/read.ts +11 -9
  186. package/database/actions/readAndWait.ts +10 -5
  187. package/database/server/agentDelegation.ts +3 -2
  188. package/database/server/authorityStoreConformance.ts +209 -0
  189. package/database/server/authorityStoreTypes.ts +31 -0
  190. package/database/server/coreDataProxy.ts +1 -1
  191. package/database/server/dataHandlers.ts +13 -5
  192. package/database/server/db.ts +15 -34
  193. package/database/server/delete.ts +88 -71
  194. package/database/server/emailRepository.ts +11 -9
  195. package/database/server/fileService.ts +2 -1
  196. package/database/server/legacyServerDb.ts +58 -0
  197. package/database/server/levelAuthorityStore.ts +75 -0
  198. package/database/server/memoryAuthorityStore.ts +133 -0
  199. package/database/server/patch.ts +13 -12
  200. package/database/server/query.ts +3 -2
  201. package/database/server/read.ts +33 -30
  202. package/database/server/resourceAccess.ts +7 -5
  203. package/database/server/serverStoreFactory.ts +124 -0
  204. package/database/server/spaceMemberAuthority.ts +3 -2
  205. package/database/server/storeBatchCompat.ts +37 -0
  206. package/database/server/testAuthorityStore.ts +76 -0
  207. package/database/server/write.ts +29 -27
  208. package/integrations/anthropic/anthropicModels.ts +12 -12
  209. package/integrations/deepseek/models.ts +67 -67
  210. package/integrations/google/models.ts +111 -53
  211. package/integrations/openai/models.ts +22 -22
  212. package/integrations/xai/models.ts +1 -1
  213. package/localRuntimeDb.ts +39 -2
  214. package/machineCommandPrompts.ts +2 -0
  215. package/machineCommands.ts +193 -63
  216. package/machineWsRunDispatch.ts +80 -15
  217. package/machineWsSession.ts +39 -2
  218. package/noloReadBridgePrompt.ts +16 -0
  219. package/package.json +99 -6
  220. package/render/layout/mainLayoutViewMode.ts +25 -0
  221. package/render/page/server/createPage.ts +3 -1
  222. package/render/page/server/handleCreatePage.ts +2 -11
  223. package/runtimeDoctorCommands.ts +15 -66
  224. package/server/api/methods.ts +34 -12
  225. package/server/api/tableHandlers.ts +6 -3
  226. package/server/authenticateToken.ts +47 -2
  227. package/server/billingLedgerRoutes.ts +15 -0
  228. package/server/buildManifest.ts +54 -0
  229. package/server/coreReadiness.ts +34 -25
  230. package/server/coreRuntime.ts +15 -10
  231. package/server/handlers/activityStatsHandler.ts +5 -2
  232. package/server/handlers/agentEmailRegistrationE2EHandler.ts +25 -23
  233. package/server/handlers/agentRun/agentLookup.ts +1 -7
  234. package/server/handlers/agentRun/autonomousRuntime.ts +11 -10
  235. package/server/handlers/agentRun/builtinAgents.ts +12 -88
  236. package/server/handlers/agentRun/continueDialogContext.ts +15 -3
  237. package/server/handlers/agentRun/{inheritedDialogPrompt.ts → conversationReference.ts} +3 -3
  238. package/server/handlers/agentRun/dialogLookup.ts +4 -3
  239. package/server/handlers/agentRun/index.ts +864 -404
  240. package/server/handlers/agentRun/localRuntimeBootstrap.ts +17 -1
  241. package/server/handlers/agentRun/loop.ts +920 -508
  242. package/server/handlers/agentRun/machineConnector.ts +56 -8
  243. package/server/handlers/agentRun/machineRunAudit.ts +3 -2
  244. package/server/handlers/agentRun/modelUsageToolHandlers.ts +13 -10
  245. package/server/handlers/agentRun/requestContext.ts +12 -8
  246. package/server/handlers/agentRun/runtimeExecutionContext.ts +15 -3
  247. package/server/handlers/agentRun/runtimePreparation.ts +26 -24
  248. package/server/handlers/agentRun/runtimeRouting.ts +25 -40
  249. package/server/handlers/agentRun/runtimeSystemMessages.ts +65 -0
  250. package/server/handlers/agentRun/saveDialog.ts +97 -87
  251. package/server/handlers/agentRun/taskRunToolHandler.ts +9 -7
  252. package/server/handlers/agentRun/toolExecutor.ts +42 -78
  253. package/server/handlers/agentRun/types.ts +2 -1
  254. package/server/handlers/appDeployStatusStore.ts +17 -10
  255. package/server/handlers/appDomainHandler.ts +21 -15
  256. package/server/handlers/appLegacyInfraCleanup.ts +5 -2
  257. package/server/handlers/appSandboxHandler.ts +29 -8
  258. package/server/handlers/appSpaceAccess.ts +15 -8
  259. package/server/handlers/chatHandler.ts +22 -2
  260. package/server/handlers/chatUpstreamRetry.ts +225 -2
  261. package/server/handlers/connector/connectorConnectionRegistry.ts +55 -7
  262. package/server/handlers/connector/connectorWebSocket.ts +14 -10
  263. package/server/handlers/desktopAgentRuntimeAdapter.ts +4 -4
  264. package/server/handlers/desktopAgentRuntimeHostCapabilities.ts +25 -0
  265. package/server/handlers/desktopAgentRuntimeReadiness.ts +62 -0
  266. package/server/handlers/desktopAgentRuntimeStatusHandler.ts +38 -30
  267. package/server/handlers/desktopAgentRuntimeTurnHandler.ts +7 -3
  268. package/server/handlers/desktopAgentRuntimeTurnService.ts +39 -45
  269. package/server/handlers/desktopLlamaRuntimeHandler.ts +85 -51
  270. package/server/handlers/desktopUpdaterHandler.ts +132 -24
  271. package/server/handlers/favoriteHandlers.ts +8 -2
  272. package/server/handlers/getDialogBillingHandler.ts +4 -3
  273. package/server/handlers/getTransactionsHandler.ts +5 -2
  274. package/server/handlers/machines/machineBinding.ts +1 -0
  275. package/server/handlers/machines/machineHandlers.ts +0 -14
  276. package/server/handlers/machines/machineInstallAssets.ts +4 -2
  277. package/server/handlers/notificationEmitter.ts +5 -3
  278. package/server/handlers/notificationsHandler.ts +5 -2
  279. package/server/handlers/providerCallsRecentHandler.ts +234 -0
  280. package/server/handlers/providerReconciliationRunHandler.ts +180 -0
  281. package/server/handlers/providerRequestIdsHandler.ts +105 -0
  282. package/server/handlers/tableAccess.ts +6 -2
  283. package/server/handlers/tableToolHandlers.ts +28 -14
  284. package/server/handlers/userSecretsHandler.ts +17 -6
  285. package/server/handlers/versionHandler.ts +9 -4
  286. package/server/handlers/versionStore.ts +18 -10
  287. package/server/html/ssrPreload.ts +5 -3
  288. package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +11 -8
  289. package/server/payments/crypto/cryptoUsdcBaseScanner.ts +12 -7
  290. package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +11 -8
  291. package/server/payments/crypto/cryptoUsdtTronScanner.ts +9 -7
  292. package/server/payments/waffo/waffoPaymentProvider.ts +12 -9
  293. package/server/runtimeRole.ts +15 -0
  294. package/server/scheduler/scheduledTaskRuntime.ts +4 -2
  295. package/server/scheduler/schedulerService.ts +2 -2
  296. package/server/taskRunBridgeRoutes.ts +8 -5
  297. package/share/server/fetchCommunityShares.ts +1 -1
  298. package/share/server/helpers.ts +19 -17
  299. package/share/server/importShare.ts +9 -13
  300. package/share/server/listCommunityShares.ts +4 -4
  301. package/share/server/purchaseDoc.ts +8 -16
  302. package/share/server/readCommunityShareCover.ts +3 -2
  303. package/share/server/readSharedTable.ts +14 -13
  304. package/share/server/tableReplication.ts +42 -39
  305. package/spaceAttachCommands.ts +135 -0
  306. package/spaceInternalCommandEntries.ts +2 -0
  307. package/ai/agent/runtimeServerBase.ts +0 -37
  308. package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
package/auth/routes.ts CHANGED
@@ -37,6 +37,46 @@ export const authRoutes = {
37
37
  method: "GET" as const,
38
38
  createPath: () => `${API_VERSION}/users`,
39
39
  },
40
+ usageReport: {
41
+ path: `${API_VERSION}/users/usage-report`,
42
+ method: "GET" as const,
43
+ createPath: () => `${API_VERSION}/users/usage-report`,
44
+ },
45
+ providerBillingHealth: {
46
+ path: `${API_VERSION}/users/provider-billing-health`,
47
+ method: "GET" as const,
48
+ createPath: () => `${API_VERSION}/users/provider-billing-health`,
49
+ },
50
+ providerBillingDrilldown: {
51
+ path: `${API_VERSION}/users/provider-billing-drilldown`,
52
+ method: "GET" as const,
53
+ createPath: () => `${API_VERSION}/users/provider-billing-drilldown`,
54
+ },
55
+ providerCredentials: {
56
+ path: `${API_VERSION}/users/provider-credentials`,
57
+ method: "GET" as const,
58
+ createPath: () => `${API_VERSION}/users/provider-credentials`,
59
+ },
60
+ providerCredentialLifecycle: {
61
+ path: `${API_VERSION}/users/provider-credentials/lifecycle`,
62
+ method: "POST" as const,
63
+ createPath: () => `${API_VERSION}/users/provider-credentials/lifecycle`,
64
+ },
65
+ billingAnomalyLifecycle: {
66
+ path: `${API_VERSION}/users/billing-anomalies/lifecycle`,
67
+ method: "POST" as const,
68
+ createPath: () => `${API_VERSION}/users/billing-anomalies/lifecycle`,
69
+ },
70
+ billingAnomalyDrilldown: {
71
+ path: `${API_VERSION}/users/billing-anomalies/drilldown`,
72
+ method: "GET" as const,
73
+ createPath: () => `${API_VERSION}/users/billing-anomalies/drilldown`,
74
+ },
75
+ billingAnomalyAudit: {
76
+ path: `${API_VERSION}/users/billing-anomalies/audit`,
77
+ method: "POST" as const,
78
+ createPath: () => `${API_VERSION}/users/billing-anomalies/audit`,
79
+ },
40
80
  detail: {
41
81
  path: `${API_VERSION}/users/:userId`,
42
82
  method: "GET" as const,
@@ -72,6 +112,12 @@ export const authRoutes = {
72
112
  createPath: (params: RouteParams) =>
73
113
  `${API_VERSION}/users/${params.userId}/enable`,
74
114
  },
115
+ adminPermissions: {
116
+ path: `${API_VERSION}/users/:userId/admin-permissions`,
117
+ method: "POST" as const,
118
+ createPath: (params: RouteParams) =>
119
+ `${API_VERSION}/users/${params.userId}/admin-permissions`,
120
+ },
75
121
  sendEmail: {
76
122
  path: `${API_VERSION}/users/send-email`,
77
123
  method: "POST" as const,
@@ -1,5 +1,5 @@
1
1
  import { DB_PREFIX } from "../../database/keys";
2
- import serverDb from "../../database/server/db";
2
+ import { getServerAuthorityStore } from "../../database/server/db";
3
3
 
4
4
  import { balanceLockManager } from "./locks";
5
5
 
@@ -116,10 +116,11 @@ export async function recordUserAccessActivity(
116
116
  ) {
117
117
  const timestamp = options.timestamp ?? Date.now();
118
118
  const key = `${DB_PREFIX.USER}${userId}`;
119
+ const store = getServerAuthorityStore();
119
120
  const lock = balanceLockManager.getLock(userId);
120
121
 
121
122
  await lock.runExclusive(async () => {
122
- const user = await serverDb.get(key).catch(() => null);
123
+ const user = await store.get(key).catch(() => null);
123
124
  if (!user) return;
124
125
 
125
126
  const entry: RecentAccessEntry = {
@@ -129,7 +130,7 @@ export async function recordUserAccessActivity(
129
130
  device: summarizeUserAgent(options.userAgent),
130
131
  };
131
132
 
132
- await serverDb.put(key, {
133
+ await store.put(key, {
133
134
  ...user,
134
135
  ...(options.updateLastActiveAt ? { lastActiveAt: timestamp } : {}),
135
136
  ...(options.updateLastLoginAt ? { lastLoginAt: timestamp } : {}),
@@ -0,0 +1,43 @@
1
+ import {
2
+ checkAdminPermission,
3
+ createErrorResponse,
4
+ createSuccessResponse,
5
+ handleOptionsRequest,
6
+ } from "./shared";
7
+ import { updateUserAdminPermissions } from "./usageManagementAccess";
8
+
9
+ export async function handleUpdateUserAdminPermissions(req: Request, userId: string) {
10
+ if (req.method === "OPTIONS") {
11
+ return handleOptionsRequest();
12
+ }
13
+
14
+ const permissionError = checkAdminPermission(req.user?.userId);
15
+ if (permissionError) return permissionError;
16
+
17
+ if (!userId) {
18
+ return createErrorResponse("User ID is required", 400);
19
+ }
20
+
21
+ try {
22
+ const body = await req.json().catch(() => ({}));
23
+ const usageManagement = body?.usageManagement;
24
+ if (typeof usageManagement !== "boolean") {
25
+ return createErrorResponse("usageManagement must be boolean", 400);
26
+ }
27
+
28
+ const updatedUser = await updateUserAdminPermissions(userId, {
29
+ usageManagement,
30
+ });
31
+ if (!updatedUser) {
32
+ return createErrorResponse("User not found", 404);
33
+ }
34
+
35
+ return createSuccessResponse({
36
+ success: true,
37
+ userId,
38
+ adminPermissions: updatedUser.adminPermissions,
39
+ });
40
+ } catch {
41
+ return createErrorResponse("Failed to update admin permissions");
42
+ }
43
+ }
@@ -0,0 +1,375 @@
1
+ import { getServerAuthorityStore } from "../../database/server/db";
2
+ import { createAgentKey, createCybotKey, createKey, DB_PREFIX } from "../../database/keys";
3
+
4
+ import {
5
+ checkAdminPermission,
6
+ createErrorResponse,
7
+ createSuccessResponse,
8
+ handleOptionsRequest,
9
+ } from "./shared";
10
+
11
+ export type AdminUsageReportStore = {
12
+ iterator: (options: { gte?: string; lte?: string }) => AsyncIterable<[string, unknown]>;
13
+ get?: (key: string) => Promise<unknown>;
14
+ };
15
+
16
+ export type AdminUsageRankItem = {
17
+ count: number;
18
+ cost: number;
19
+ inputTokens: number;
20
+ outputTokens: number;
21
+ cacheCreationInputTokens: number;
22
+ cacheReadInputTokens: number;
23
+ costRatio: number;
24
+ };
25
+
26
+ export type AdminUsageReport = {
27
+ generatedAt: string;
28
+ sinceMs: number;
29
+ untilMs: number;
30
+ limit: number;
31
+ summary: AdminUsageRankItem & {
32
+ recordsScanned: number;
33
+ recordsInWindow: number;
34
+ totalCost: number;
35
+ };
36
+ rankings: {
37
+ users: (AdminUsageRankItem & {
38
+ userId: string;
39
+ username?: string;
40
+ email?: string;
41
+ })[];
42
+ dialogs: (AdminUsageRankItem & {
43
+ dialogId: string;
44
+ userId: string;
45
+ username?: string;
46
+ title?: string;
47
+ })[];
48
+ models: (AdminUsageRankItem & {
49
+ provider: string;
50
+ model: string;
51
+ serviceTier?: string;
52
+ })[];
53
+ agents: (AdminUsageRankItem & {
54
+ agentKey: string;
55
+ name?: string;
56
+ title?: string;
57
+ })[];
58
+ };
59
+ };
60
+
61
+ type TokenRecord = {
62
+ type?: string;
63
+ userId?: string;
64
+ username?: string;
65
+ cybotId?: string;
66
+ provider?: string;
67
+ model?: string;
68
+ billing_service_tier?: string;
69
+ dialogId?: string;
70
+ input_tokens?: number;
71
+ output_tokens?: number;
72
+ cache_creation_input_tokens?: number;
73
+ cache_read_input_tokens?: number;
74
+ cost?: number;
75
+ timestamp?: number;
76
+ createdAt?: string | number;
77
+ };
78
+
79
+ type Accumulator = {
80
+ count: number;
81
+ cost: number;
82
+ inputTokens: number;
83
+ outputTokens: number;
84
+ cacheCreationInputTokens: number;
85
+ cacheReadInputTokens: number;
86
+ };
87
+
88
+ const TOKEN_PREFIX = "token-";
89
+ const TOKEN_STATS_PREFIX = "token-stats-";
90
+ const MAX_LIMIT = 200;
91
+
92
+ const rangeEnd = (prefix: string) => `${prefix}\uffff`;
93
+
94
+ const roundMetric = (value: number) => Math.round(value * 1_000_000) / 1_000_000;
95
+
96
+ const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
97
+ !!value && typeof value === "object" && !Array.isArray(value);
98
+
99
+ const toFiniteNumber = (value: unknown, fallback = 0) =>
100
+ typeof value === "number" && Number.isFinite(value) ? value : fallback;
101
+
102
+ const normalizeText = (value: unknown, fallback = "unknown") =>
103
+ typeof value === "string" && value.trim() ? value.trim() : fallback;
104
+
105
+ const createAccumulator = (): Accumulator => ({
106
+ count: 0,
107
+ cost: 0,
108
+ inputTokens: 0,
109
+ outputTokens: 0,
110
+ cacheCreationInputTokens: 0,
111
+ cacheReadInputTokens: 0,
112
+ });
113
+
114
+ const addToAccumulator = (acc: Accumulator, record: TokenRecord) => {
115
+ acc.count += 1;
116
+ acc.cost += toFiniteNumber(record.cost);
117
+ acc.inputTokens += toFiniteNumber(record.input_tokens);
118
+ acc.outputTokens += toFiniteNumber(record.output_tokens);
119
+ acc.cacheCreationInputTokens += toFiniteNumber(record.cache_creation_input_tokens);
120
+ acc.cacheReadInputTokens += toFiniteNumber(record.cache_read_input_tokens);
121
+ };
122
+
123
+ const getTokenTimestamp = (record: TokenRecord) => {
124
+ if (typeof record.timestamp === "number" && Number.isFinite(record.timestamp)) {
125
+ return record.timestamp;
126
+ }
127
+ if (typeof record.createdAt === "number" && Number.isFinite(record.createdAt)) {
128
+ return record.createdAt;
129
+ }
130
+ if (typeof record.createdAt === "string") {
131
+ const parsed = Date.parse(record.createdAt);
132
+ if (Number.isFinite(parsed)) return parsed;
133
+ }
134
+ return null;
135
+ };
136
+
137
+ const isTokenRecord = (key: string, value: unknown): value is TokenRecord =>
138
+ key.startsWith(TOKEN_PREFIX) &&
139
+ !key.startsWith(TOKEN_STATS_PREFIX) &&
140
+ isObjectRecord(value) &&
141
+ value.type === "token";
142
+
143
+ const metricFields = (acc: Accumulator, totalCost: number): AdminUsageRankItem => ({
144
+ count: acc.count,
145
+ cost: roundMetric(acc.cost),
146
+ inputTokens: acc.inputTokens,
147
+ outputTokens: acc.outputTokens,
148
+ cacheCreationInputTokens: acc.cacheCreationInputTokens,
149
+ cacheReadInputTokens: acc.cacheReadInputTokens,
150
+ costRatio: totalCost > 0 ? roundMetric(acc.cost / totalCost) : 0,
151
+ });
152
+
153
+ const sortedEntries = <T>(groups: Map<string, T & Accumulator>, limit: number) =>
154
+ Array.from(groups.entries())
155
+ .sort(([, a], [, b]) => b.cost - a.cost || b.outputTokens - a.outputTokens || b.count - a.count)
156
+ .slice(0, limit);
157
+
158
+ const getOptional = async (store: AdminUsageReportStore, key: string) => {
159
+ if (!store.get) return null;
160
+ try {
161
+ return await store.get(key);
162
+ } catch {
163
+ return null;
164
+ }
165
+ };
166
+
167
+ const readUserLabel = async (store: AdminUsageReportStore, userId: string) => {
168
+ const value = await getOptional(store, `${DB_PREFIX.USER}${userId}`);
169
+ if (!isObjectRecord(value)) return {};
170
+ return {
171
+ username: typeof value.username === "string" ? value.username : undefined,
172
+ email: typeof value.email === "string" ? value.email : undefined,
173
+ };
174
+ };
175
+
176
+ const readDialogTitle = async (
177
+ store: AdminUsageReportStore,
178
+ userId: string,
179
+ dialogId: string
180
+ ) => {
181
+ const value = await getOptional(store, createKey("dialog", userId, dialogId));
182
+ if (!isObjectRecord(value)) return undefined;
183
+ return typeof value.title === "string" ? value.title : undefined;
184
+ };
185
+
186
+ const readAgentLabel = async (store: AdminUsageReportStore, agentKey: string) => {
187
+ const candidateKeys = [agentKey];
188
+ if (!agentKey.startsWith("agent-") && !agentKey.startsWith("cybot-")) {
189
+ candidateKeys.push(createAgentKey.public(agentKey), createCybotKey.public(agentKey));
190
+ }
191
+
192
+ for (const key of candidateKeys) {
193
+ const value = await getOptional(store, key);
194
+ if (!isObjectRecord(value)) continue;
195
+ return {
196
+ name: typeof value.name === "string" ? value.name : undefined,
197
+ title: typeof value.title === "string" ? value.title : undefined,
198
+ };
199
+ }
200
+
201
+ return {};
202
+ };
203
+
204
+ const addGroupRecord = <T extends object>(
205
+ groups: Map<string, T & Accumulator>,
206
+ key: string,
207
+ record: TokenRecord,
208
+ meta: T
209
+ ) => {
210
+ const existing = groups.get(key);
211
+ const acc = existing ?? ({ ...meta, ...createAccumulator() } as T & Accumulator);
212
+ addToAccumulator(acc, record);
213
+ groups.set(key, acc);
214
+ };
215
+
216
+ export async function buildAdminUsageReport(args: {
217
+ store: AdminUsageReportStore;
218
+ sinceMs: number;
219
+ untilMs: number;
220
+ nowMs?: number;
221
+ limit?: number;
222
+ }): Promise<AdminUsageReport> {
223
+ const limit = Math.min(MAX_LIMIT, Math.max(1, Math.floor(args.limit ?? 20)));
224
+ const totals = createAccumulator();
225
+ const users = new Map<string, { userId: string; username?: string } & Accumulator>();
226
+ const dialogs = new Map<
227
+ string,
228
+ { userId: string; username?: string; dialogId: string } & Accumulator
229
+ >();
230
+ const models = new Map<
231
+ string,
232
+ { provider: string; model: string; serviceTier?: string } & Accumulator
233
+ >();
234
+ const agents = new Map<string, { agentKey: string } & Accumulator>();
235
+ let recordsScanned = 0;
236
+
237
+ for await (const [key, value] of args.store.iterator({
238
+ gte: TOKEN_PREFIX,
239
+ lte: rangeEnd(TOKEN_PREFIX),
240
+ })) {
241
+ if (!isTokenRecord(key, value)) continue;
242
+ recordsScanned += 1;
243
+
244
+ const timestamp = getTokenTimestamp(value);
245
+ if (timestamp === null || timestamp < args.sinceMs || timestamp > args.untilMs) {
246
+ continue;
247
+ }
248
+
249
+ addToAccumulator(totals, value);
250
+
251
+ const userId = normalizeText(value.userId);
252
+ const username = typeof value.username === "string" ? value.username : undefined;
253
+ const dialogId = normalizeText(value.dialogId);
254
+ const provider = normalizeText(value.provider);
255
+ const model = normalizeText(value.model);
256
+ const serviceTier =
257
+ typeof value.billing_service_tier === "string" && value.billing_service_tier.trim()
258
+ ? value.billing_service_tier.trim()
259
+ : undefined;
260
+ const agentKey = normalizeText(value.cybotId);
261
+
262
+ addGroupRecord(users, userId, value, { userId, username });
263
+ addGroupRecord(dialogs, `${userId}\u0000${dialogId}`, value, {
264
+ userId,
265
+ username,
266
+ dialogId,
267
+ });
268
+ addGroupRecord(models, `${provider}\u0000${model}\u0000${serviceTier ?? ""}`, value, {
269
+ provider,
270
+ model,
271
+ serviceTier,
272
+ });
273
+ addGroupRecord(agents, agentKey, value, { agentKey });
274
+ }
275
+
276
+ const totalCost = roundMetric(totals.cost);
277
+ const userLabelCache = new Map<string, Awaited<ReturnType<typeof readUserLabel>>>();
278
+ const getUserLabel = async (userId: string) => {
279
+ if (!userLabelCache.has(userId)) {
280
+ userLabelCache.set(userId, await readUserLabel(args.store, userId));
281
+ }
282
+ return userLabelCache.get(userId) ?? {};
283
+ };
284
+
285
+ return {
286
+ generatedAt: new Date(args.nowMs ?? Date.now()).toISOString(),
287
+ sinceMs: args.sinceMs,
288
+ untilMs: args.untilMs,
289
+ limit,
290
+ summary: {
291
+ ...metricFields(totals, totalCost),
292
+ recordsScanned,
293
+ recordsInWindow: totals.count,
294
+ totalCost,
295
+ },
296
+ rankings: {
297
+ users: await Promise.all(
298
+ sortedEntries(users, limit).map(async ([, acc]) => {
299
+ const userLabel = await getUserLabel(acc.userId);
300
+ return {
301
+ ...metricFields(acc, totalCost),
302
+ userId: acc.userId,
303
+ email: userLabel.email,
304
+ username: userLabel.username ?? acc.username,
305
+ };
306
+ })
307
+ ),
308
+ dialogs: await Promise.all(
309
+ sortedEntries(dialogs, limit).map(async ([, acc]) => {
310
+ const userLabel = await getUserLabel(acc.userId);
311
+ return {
312
+ ...metricFields(acc, totalCost),
313
+ userId: acc.userId,
314
+ username: userLabel.username ?? acc.username,
315
+ dialogId: acc.dialogId,
316
+ title: await readDialogTitle(args.store, acc.userId, acc.dialogId),
317
+ };
318
+ })
319
+ ),
320
+ models: sortedEntries(models, limit).map(([, acc]) => ({
321
+ ...metricFields(acc, totalCost),
322
+ provider: acc.provider,
323
+ model: acc.model,
324
+ serviceTier: acc.serviceTier,
325
+ })),
326
+ agents: await Promise.all(
327
+ sortedEntries(agents, limit).map(async ([, acc]) => ({
328
+ ...metricFields(acc, totalCost),
329
+ agentKey: acc.agentKey,
330
+ ...(await readAgentLabel(args.store, acc.agentKey)),
331
+ }))
332
+ ),
333
+ },
334
+ };
335
+ }
336
+
337
+ const parseMsParam = (value: string | null) => {
338
+ if (!value) return null;
339
+ const asNumber = Number(value);
340
+ if (Number.isFinite(asNumber)) return asNumber;
341
+ const parsed = Date.parse(value);
342
+ return Number.isFinite(parsed) ? parsed : null;
343
+ };
344
+
345
+ export async function handleAdminUsageReport(req: Request) {
346
+ if (req.method === "OPTIONS") {
347
+ return handleOptionsRequest();
348
+ }
349
+
350
+ const { userId: actionUserId } = req.user;
351
+ const permissionError = checkAdminPermission(actionUserId);
352
+ if (permissionError) return permissionError;
353
+
354
+ try {
355
+ const url = new URL(req.url);
356
+ const sinceMs = parseMsParam(url.searchParams.get("sinceMs"));
357
+ const untilMs = parseMsParam(url.searchParams.get("untilMs"));
358
+ const limit = Number(url.searchParams.get("limit") || "20");
359
+
360
+ if (sinceMs === null || untilMs === null || sinceMs > untilMs) {
361
+ return createErrorResponse("Invalid usage report time range", 400);
362
+ }
363
+
364
+ const report = await buildAdminUsageReport({
365
+ store: getServerAuthorityStore(),
366
+ sinceMs,
367
+ untilMs,
368
+ limit,
369
+ });
370
+
371
+ return createSuccessResponse(report);
372
+ } catch {
373
+ return createErrorResponse("Internal server error");
374
+ }
375
+ }