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
@@ -1,7 +1,6 @@
1
1
  // auth/handleToken.js
2
- import { verifyToken, parseToken } from "../token";
3
- import { assertSessionTokenVersion } from "../sessionTokenVersion";
4
- import serverDb from "../../database/server/db";
2
+ import { getServerAuthorityStore } from "../../database/server/db";
3
+ import { authenticateToken } from "../../server/authenticateToken";
5
4
  import { balanceLockManager } from "./locks";
6
5
  import { recordUserAccessActivity } from "./accessActivity";
7
6
 
@@ -15,21 +14,14 @@ const getUserActivityUpdateIntervalMs = () => {
15
14
  : 5 * 60 * 1000;
16
15
  };
17
16
 
18
- const getPublicKey = async (userId) => {
19
- try {
20
- const user = await serverDb.get(`user:${userId}`);
21
- if (user?.publicKey) return user.publicKey;
22
- } catch { }
23
- return null;
24
- };
25
-
26
17
  const updateUserLastActiveAt = async (userId, metadata?: { ip?: string | null; userAgent?: string | null }) => {
18
+ const store = getServerAuthorityStore();
27
19
  const key = `user:${userId}`;
28
20
  const lock = balanceLockManager.getLock(userId);
29
21
  let shouldRecord = false;
30
22
  try {
31
23
  await lock.runExclusive(async () => {
32
- const user = await serverDb.get(key);
24
+ const user = await store.get(key);
33
25
  if (!user) return;
34
26
 
35
27
  const now = Date.now();
@@ -57,53 +49,59 @@ const updateUserLastActiveAt = async (userId, metadata?: { ip?: string | null; u
57
49
  }
58
50
  };
59
51
 
60
- // NOTE: 这里用 new Date(exp) 处理 ISO 字符串,
61
- // 而 verifyToken 只检查 number 类型 exp(见 auth/token.ts 的 BUG 注释)
62
- // 两套逻辑不一致,待 token.ts 的 exp 检查统一修复后可删除此函数
63
- const isTokenExpired = ({ exp, nbf }) => {
64
- const now = Date.now();
65
- return (exp && now > exp * 1000) || (nbf && now < nbf * 1000);
66
- };
67
-
68
- export async function handleToken(req, res) {
69
- const token = req.headers.get("authorization")?.split(" ")[1];
70
- const requestIp =
71
- req.headers.get("x-nolo-client-ip")?.trim() ||
72
- req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
73
- req.headers.get("x-real-ip") ||
74
- "";
75
- const userAgent = req.headers.get("user-agent") || "";
76
- if (!token) return handleError(res, new Error("Access denied. No token provided."), 401);
77
-
78
- try {
79
- // parseToken 使用 js-base64,正确处理中文用户名的 UTF-8 编码
80
- // 原 atob 方案在中文用户名时会产生乱码,导致 userId 错误、publicKey 查找失败
81
- const parsed = parseToken(token);
82
- if (!parsed?.userId) return handleError(res, new Error("Invalid token format"), 401);
83
- const { userId } = parsed;
84
- const publicKey = await getPublicKey(userId);
85
-
86
- if (!publicKey) return handleError(res, new Error("Public key not found"), 401);
52
+ const readBearerToken = (headers: { get(name: string): string | null | undefined }) =>
53
+ headers.get("authorization")?.split(" ")[1];
87
54
 
88
- const user = await serverDb.get(`user:${userId}`);
89
- if (!user?.publicKey) {
90
- return handleError(res, new Error("Public key not found"), 401);
91
- }
55
+ const readRequestMetadata = (headers: { get(name: string): string | null | undefined }) => ({
56
+ ip:
57
+ headers.get("x-nolo-client-ip")?.trim() ||
58
+ headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
59
+ headers.get("x-real-ip") ||
60
+ "",
61
+ userAgent: headers.get("user-agent") || "",
62
+ });
92
63
 
93
- const payload = verifyToken(token, publicKey);
94
- assertSessionTokenVersion(payload, user);
95
- // NOTE: verifyToken 验签失败会直接 throw,不会返回 null/falsy
96
- // 所以下面这行是死代码,永远不会执行
97
- if (!payload) return handleError(res, new Error("Invalid token"), 401);
64
+ type TokenAuthResult =
65
+ | { ok: true; payload: any }
66
+ | { ok: false; error: Error; status: number };
98
67
 
99
- // NOTE: verifyToken 内部已检查 Unix 秒格式的 exp,这里再检查 ISO 字符串格式
100
- // 两处检查逻辑不同,待 token.ts 修复后可删除此行
101
- if (isTokenExpired(payload)) return handleError(res, new Error("Token has expired"), 401);
68
+ async function authenticateRequestHeaders(
69
+ headers: { get(name: string): string | null | undefined }
70
+ ): Promise<TokenAuthResult> {
71
+ const token = readBearerToken(headers);
72
+ if (!token) {
73
+ return {
74
+ ok: false,
75
+ error: new Error("Access denied. No token provided."),
76
+ status: 401,
77
+ };
78
+ }
102
79
 
103
- await updateUserLastActiveAt(userId, { ip: requestIp, userAgent });
80
+ try {
81
+ const { userId, payload } = await authenticateToken(token);
82
+ const { ip, userAgent } = readRequestMetadata(headers);
83
+ await updateUserLastActiveAt(userId, { ip, userAgent });
84
+ return { ok: true, payload };
85
+ } catch (error: any) {
86
+ return {
87
+ ok: false,
88
+ error: error instanceof Error ? error : new Error(String(error)),
89
+ status: 401,
90
+ };
91
+ }
92
+ }
104
93
 
105
- return payload;
106
- } catch (err) {
107
- return handleError(res, err, 401);
94
+ export async function handleToken(req, res) {
95
+ const result = await authenticateRequestHeaders(req.headers);
96
+ if (!result.ok) {
97
+ return handleError(res, result.error, result.status);
108
98
  }
99
+ return result.payload;
100
+ }
101
+
102
+ export async function readUserFromRequest(
103
+ req: Pick<Request, "headers"> | { headers: { get(name: string): string | null | undefined } }
104
+ ) {
105
+ const result = await authenticateRequestHeaders(req.headers);
106
+ return result.ok ? result.payload : null;
109
107
  }
@@ -1,5 +1,6 @@
1
1
  import { DB_PREFIX, createTransactionKey } from "../../database/keys";
2
- import serverDb from "../../database/server/db";
2
+ import { getServerAuthorityStore } from "../../database/server/db";
3
+ import { writeStoreOps } from "../../database/server/storeBatchCompat";
3
4
 
4
5
  import { balanceLockManager } from "./locks";
5
6
  import {
@@ -49,6 +50,14 @@ export type TokenUsageChargeResult =
49
50
  balance?: number;
50
51
  };
51
52
 
53
+ const createTokenUsageBillingStore = (authorityStore = getServerAuthorityStore()): TokenUsageBillingStore => ({
54
+ get: (key) => authorityStore.get(key),
55
+ batch: (ops) => writeStoreOps(authorityStore, ops),
56
+ });
57
+
58
+ const getDefaultTokenUsageBillingStore = (): TokenUsageBillingStore =>
59
+ createTokenUsageBillingStore(getServerAuthorityStore());
60
+
52
61
  const getMaybe = async <T>(
53
62
  store: TokenUsageBillingStore,
54
63
  key: string
@@ -64,13 +73,15 @@ const isPositiveFinite = (value: unknown): value is number =>
64
73
  typeof value === "number" && Number.isFinite(value) && value > 0;
65
74
 
66
75
  export async function chargeTokenUsageWithLedger({
67
- store = serverDb,
76
+ store = getDefaultTokenUsageBillingStore(),
68
77
  runtime = getBillingLedgerRuntime(),
69
78
  userId,
70
79
  tokenKey,
71
80
  tokenRecord,
72
81
  reason = "token_usage",
73
82
  txId = `token-${tokenKey}`,
83
+ ledgerIdempotencyKey = `token:${tokenKey}`,
84
+ allowBuyerNegative = true,
74
85
  nowMs = Date.now(),
75
86
  }: {
76
87
  store?: TokenUsageBillingStore;
@@ -80,6 +91,8 @@ export async function chargeTokenUsageWithLedger({
80
91
  tokenRecord: TokenRevenueShareRecord;
81
92
  reason?: string;
82
93
  txId?: string;
94
+ ledgerIdempotencyKey?: string;
95
+ allowBuyerNegative?: boolean;
83
96
  nowMs?: number;
84
97
  }): Promise<TokenUsageChargeResult> {
85
98
  const lock = balanceLockManager.getLock(`token-usage:${userId}:${tokenKey}`);
@@ -159,7 +172,7 @@ export async function chargeTokenUsageWithLedger({
159
172
  runtime,
160
173
  type: "usage_charge",
161
174
  reason,
162
- idempotencyKey: `token:${tokenKey}`,
175
+ idempotencyKey: ledgerIdempotencyKey,
163
176
  txId,
164
177
  entries: [
165
178
  {
@@ -167,6 +180,7 @@ export async function chargeTokenUsageWithLedger({
167
180
  ownerUserId: userId,
168
181
  delta: -event.grossCredits,
169
182
  startingBalance: userData.balance,
183
+ allowNegative: allowBuyerNegative,
170
184
  metadata: {
171
185
  role: "buyer_debit",
172
186
  tokenKey,
@@ -217,10 +231,7 @@ export async function chargeTokenUsageWithLedger({
217
231
  amountCredits: event.grossCredits,
218
232
  };
219
233
  }
220
- if (
221
- ledgerAppend.status === "insufficient_funds" ||
222
- userData.balance < event.grossCredits
223
- ) {
234
+ if (ledgerAppend.status === "insufficient_funds") {
224
235
  return {
225
236
  success: false,
226
237
  status: "insufficient_funds",
@@ -0,0 +1,43 @@
1
+ import { isSystemAdmin } from "../../core/init";
2
+ import { DB_PREFIX } from "../../database/keys";
3
+ import { getServerAuthorityStore } from "../../database/server/db";
4
+ import {
5
+ hasUsageManagementPermission,
6
+ mergeAdminPermissions,
7
+ type AdminPermissions,
8
+ } from "../adminPermissions";
9
+
10
+ type UserStore = {
11
+ get(key: string): Promise<any>;
12
+ put(key: string, value: any): Promise<void>;
13
+ };
14
+
15
+ const userKey = (userId: string) => `${DB_PREFIX.USER}${userId}`;
16
+
17
+ export async function canManageUsageReportAccess(
18
+ userId?: string | null,
19
+ store: Pick<UserStore, "get"> = getServerAuthorityStore()
20
+ ): Promise<boolean> {
21
+ if (!userId) return false;
22
+ if (isSystemAdmin(userId)) return true;
23
+ const userData = await store.get(userKey(userId)).catch(() => null);
24
+ return hasUsageManagementPermission(userData);
25
+ }
26
+
27
+ export async function updateUserAdminPermissions(
28
+ targetUserId: string,
29
+ patch: Partial<AdminPermissions>,
30
+ store: UserStore = getServerAuthorityStore()
31
+ ) {
32
+ const key = userKey(targetUserId);
33
+ const userData = await store.get(key).catch(() => null);
34
+ if (!userData) return null;
35
+ const adminPermissions = mergeAdminPermissions(userData.adminPermissions, patch);
36
+ const updatedUser = {
37
+ ...userData,
38
+ adminPermissions,
39
+ adminPermissionsUpdatedAt: Date.now(),
40
+ };
41
+ await store.put(key, updatedUser);
42
+ return updatedUser;
43
+ }
@@ -0,0 +1,59 @@
1
+ import { getServerAuthorityStore } from "../../database/server/db";
2
+ import { buildBillingUsageReport } from "./billingUsageReport";
3
+ import {
4
+ createErrorResponse,
5
+ createSuccessResponse,
6
+ handleOptionsRequest,
7
+ } from "./shared";
8
+ import { canManageUsageReportAccess } from "./usageManagementAccess";
9
+
10
+ const parseTimestamp = (value: string | null) => {
11
+ if (!value) return null;
12
+ const trimmed = value.trim();
13
+ if (!trimmed) return null;
14
+ if (/^\d+$/.test(trimmed)) return Number(trimmed);
15
+ const parsed = Date.parse(trimmed);
16
+ return Number.isFinite(parsed) ? parsed : null;
17
+ };
18
+
19
+ const parseSinceDays = (value: string | null) => {
20
+ if (!value) return 7;
21
+ const parsed = Number(value);
22
+ if (!Number.isFinite(parsed) || parsed <= 0) return 7;
23
+ return Math.min(parsed, 3660);
24
+ };
25
+
26
+ export async function handleGetUsageReport(req: Request) {
27
+ if (req.method === "OPTIONS") {
28
+ return handleOptionsRequest();
29
+ }
30
+
31
+ const { userId: actionUserId } = req.user;
32
+ if (!(await canManageUsageReportAccess(actionUserId))) {
33
+ return createErrorResponse("Unauthorized: Usage management access required", 403);
34
+ }
35
+
36
+ try {
37
+ const url = new URL(req.url);
38
+ const now = Date.now();
39
+ const untilMs = parseTimestamp(url.searchParams.get("until")) ?? now;
40
+ const sinceMs =
41
+ parseTimestamp(url.searchParams.get("since")) ??
42
+ untilMs - parseSinceDays(url.searchParams.get("sinceDays")) * 24 * 3600_000;
43
+
44
+ if (sinceMs > untilMs) {
45
+ return createErrorResponse("Invalid time range", 400);
46
+ }
47
+
48
+ const report = await buildBillingUsageReport({
49
+ store: getServerAuthorityStore(),
50
+ sinceMs,
51
+ untilMs,
52
+ nowMs: now,
53
+ });
54
+
55
+ return createSuccessResponse({ success: true, report });
56
+ } catch {
57
+ return createErrorResponse("Internal server error");
58
+ }
59
+ }
package/auth/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  // auth/types.ts
2
+ import type { AdminPermissions } from "./adminPermissions";
2
3
 
3
4
  export interface User {
4
5
  userId: string;
@@ -10,6 +11,17 @@ export interface User {
10
11
  locale?: string;
11
12
  publicKey?: string;
12
13
  tokenVersion?: number;
14
+ balance?: number;
15
+ gptProAccess?: {
16
+ status?: string;
17
+ requiredRechargeAmount?: number;
18
+ rechargeAmount?: number;
19
+ source?: string;
20
+ sourceTxId?: string;
21
+ grantedAt?: number;
22
+ updatedAt?: number;
23
+ };
24
+ adminPermissions?: AdminPermissions;
13
25
  }
14
26
  export interface TokenManager {
15
27
  getTokens(): Promise<string[]>;
package/auth/utils.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  // path auth/utils.ts
2
- import { parseToken, verifyToken } from ".//token";
3
- import { assertSessionTokenVersion } from ".//sessionTokenVersion";
4
- import serverDb from "../database/server/db";
5
- import { WEB_AUTH_TOKEN_COOKIE } from "./tokenCookie";
2
+ import { parseToken, verifyToken } from ".//token";
3
+ import { assertSessionTokenVersion } from ".//sessionTokenVersion";
4
+ import { getServerAuthorityStore } from "../database/server/db";
5
+ import { WEB_AUTH_TOKEN_COOKIE } from "./tokenCookie";
6
6
 
7
7
  export const CORS_HEADERS_AUTH = {
8
8
  "Access-Control-Allow-Origin": "*",
@@ -11,22 +11,24 @@ export const CORS_HEADERS_AUTH = {
11
11
  "Access-Control-Max-Age": "86400",
12
12
  };
13
13
 
14
- export const authErrorResponse = (
15
- msg: string,
16
- code: string,
14
+ export const authErrorResponse = (
15
+ msg: string,
16
+ code: string,
17
17
  status: number = 500
18
18
  ): Response =>
19
19
  new Response(JSON.stringify({ error: { message: msg, code } }), {
20
20
  status,
21
21
  headers: { "Content-Type": "application/json", ...CORS_HEADERS_AUTH },
22
- });
23
-
24
- const getPublicKey = async (userId: string) => {
25
- try {
26
- const u = await serverDb.get(`user:${userId}`);
27
- if (!u?.publicKey || u.balance <= 0 || u.isDisabled) {
28
- return { publicKey: null, isValid: false };
29
- }
22
+ });
23
+
24
+ const getAuthUtilsStore = () => getServerAuthorityStore();
25
+
26
+ const getPublicKey = async (userId: string) => {
27
+ try {
28
+ const u = await getAuthUtilsStore().get(`user:${userId}`);
29
+ if (!u?.publicKey || u.balance <= 0 || u.isDisabled) {
30
+ return { publicKey: null, isValid: false };
31
+ }
30
32
  return { publicKey: u.publicKey, isValid: true };
31
33
  } catch (e) {
32
34
  return { publicKey: null, isValid: false };
@@ -66,13 +68,13 @@ export const authenticateRequest = async (
66
68
  );
67
69
  }
68
70
 
69
- const { publicKey, isValid } = await getPublicKey(userIdFromTokenHeader);
70
- if (!isValid || !publicKey) {
71
- return authErrorResponse("Invalid account.", "AUTH_ACCOUNT_INVALID", 401);
72
- }
73
-
74
- const user = await serverDb.get(`user:${userIdFromTokenHeader}`);
75
- const decodedTokenData = verifyToken(token, publicKey);
71
+ const { publicKey, isValid } = await getPublicKey(userIdFromTokenHeader);
72
+ if (!isValid || !publicKey) {
73
+ return authErrorResponse("Invalid account.", "AUTH_ACCOUNT_INVALID", 401);
74
+ }
75
+
76
+ const user = await getAuthUtilsStore().get(`user:${userIdFromTokenHeader}`);
77
+ const decodedTokenData = verifyToken(token, publicKey);
76
78
  assertSessionTokenVersion(decodedTokenData, user);
77
79
  if (!decodedTokenData) {
78
80
  return authErrorResponse(
package/authCommands.ts CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  loadProfileConfig,
10
10
  saveDefaultProfile,
11
11
  } from "./client/profileConfig";
12
+ import { parseUserIdFromAuthToken } from "./cliEnvHelpers";
12
13
  import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
13
14
 
14
15
  function getArg(args: string[], flag: string) {
@@ -67,17 +68,47 @@ async function saveTokenLogin(args: {
67
68
  configPath: string;
68
69
  serverUrl: string;
69
70
  authToken: string;
71
+ fetchImpl: typeof fetch;
70
72
  output: Pick<Console, "log">;
71
73
  error: Pick<Console, "error">;
72
74
  }) {
73
- if (!args.authToken.trim()) {
75
+ const authToken = args.authToken.trim();
76
+ if (!authToken) {
74
77
  args.error.error("No auth token provided.");
75
78
  return 1;
76
79
  }
77
80
 
81
+ const userId = parseUserIdFromAuthToken(authToken);
82
+ if (!userId) {
83
+ args.error.error("Auth token payload is missing userId.");
84
+ return 1;
85
+ }
86
+
87
+ try {
88
+ const res = await args.fetchImpl(
89
+ `${args.serverUrl}/api/v1/db/query/${encodeURIComponent(userId)}?limit=1`,
90
+ {
91
+ method: "POST",
92
+ headers: {
93
+ Authorization: `Bearer ${authToken}`,
94
+ "Content-Type": "application/json",
95
+ },
96
+ body: JSON.stringify({ type: "agent" }),
97
+ }
98
+ );
99
+ if (res.status === 401 || res.status === 403) {
100
+ args.error.error(
101
+ `Auth token is not valid for ${args.serverUrl}. Log in against the matching server or use a token issued by that server.`
102
+ );
103
+ return 1;
104
+ }
105
+ } catch {
106
+ // Allow saving when the server is temporarily unreachable.
107
+ }
108
+
78
109
  saveDefaultProfile(args.configPath, {
79
110
  serverUrl: args.serverUrl,
80
- authToken: args.authToken.trim(),
111
+ authToken,
81
112
  });
82
113
  args.output.log(`Saved profile default -> ${args.serverUrl}`);
83
114
  return 0;
@@ -144,6 +175,7 @@ async function runWebLogin(args: {
144
175
  configPath: args.configPath,
145
176
  serverUrl: approvedServer,
146
177
  authToken: poll.token,
178
+ fetchImpl: args.fetchImpl,
147
179
  output: args.output,
148
180
  error: args.error,
149
181
  });
@@ -173,6 +205,7 @@ export async function runLoginCommand(args: string[], deps: LoginCommandDeps = {
173
205
  configPath,
174
206
  serverUrl,
175
207
  authToken: tokenArg,
208
+ fetchImpl: deps.fetchImpl ?? fetch,
176
209
  output: outputTarget,
177
210
  error: errorTarget,
178
211
  });
@@ -187,6 +220,7 @@ export async function runLoginCommand(args: string[], deps: LoginCommandDeps = {
187
220
  configPath,
188
221
  serverUrl,
189
222
  authToken,
223
+ fetchImpl: deps.fetchImpl ?? fetch,
190
224
  output: outputTarget,
191
225
  error: errorTarget,
192
226
  });
package/buildPublish.ts CHANGED
@@ -73,10 +73,12 @@ export async function buildPublishArtifact(
73
73
  );
74
74
 
75
75
  if (inlinedDeps.length > 0) {
76
- publishManifest.files = publishManifest.files || [];
76
+ publishManifest.files = Array.from(new Set(resolvedFilesToCopy));
77
77
  for (const dep of inlinedDeps) {
78
78
  publishManifest.files.push(`${dep}/**/*.ts`);
79
79
  }
80
+ } else {
81
+ publishManifest.files = Array.from(new Set(resolvedFilesToCopy));
80
82
  }
81
83
 
82
84
  if (Object.keys(externalDeps).length > 0) {
@@ -11,6 +11,7 @@ import {
11
11
  import { dirname, join, relative } from "node:path";
12
12
  import {
13
13
  collectTypeScriptSourceFiles,
14
+ extractLocalRelativeImports,
14
15
  findTypeScriptFiles,
15
16
  } from "./buildPublishImportAnalysis";
16
17
 
@@ -71,7 +72,10 @@ export async function buildPublishArtifactAssembly(
71
72
  rmSync(distDir, { recursive: true, force: true });
72
73
  mkdirSync(distDir, { recursive: true });
73
74
 
74
- const resolvedFilesToCopy = resolveFilesToCopy(sourceDir, filesToCopy);
75
+ const resolvedFilesToCopy = expandLocalFileDependencies(
76
+ sourceDir,
77
+ resolveFilesToCopy(sourceDir, filesToCopy)
78
+ );
75
79
  const workspaceDeps = collectWorkspaceDependencies(sourceDir, resolvedFilesToCopy);
76
80
 
77
81
  for (const file of resolvedFilesToCopy) {
@@ -288,6 +292,63 @@ async function discoverExternalImportsFromInlinedFiles(
288
292
 
289
293
  export { collectTypeScriptSourceFiles, findTypeScriptFiles };
290
294
 
295
+ export function expandLocalFileDependencies(sourceDir: string, filesToCopy: string[]): string[] {
296
+ const resolved = new Set(filesToCopy);
297
+ const pending = filesToCopy.filter((file) => file.endsWith(".ts"));
298
+
299
+ while (pending.length > 0) {
300
+ const relativeFilePath = pending.pop()!;
301
+ const absoluteFilePath = join(sourceDir, relativeFilePath);
302
+ if (!existsSync(absoluteFilePath)) {
303
+ continue;
304
+ }
305
+
306
+ const content = readFileSync(absoluteFilePath, "utf8");
307
+ for (const importPath of extractLocalRelativeImports(content)) {
308
+ const discoveredPath = resolveLocalImportPath(sourceDir, relativeFilePath, importPath);
309
+ if (!discoveredPath || resolved.has(discoveredPath)) {
310
+ continue;
311
+ }
312
+ resolved.add(discoveredPath);
313
+ if (discoveredPath.endsWith(".ts")) {
314
+ pending.push(discoveredPath);
315
+ }
316
+ }
317
+ }
318
+
319
+ return Array.from(resolved);
320
+ }
321
+
322
+ function resolveLocalImportPath(
323
+ sourceDir: string,
324
+ relativeFilePath: string,
325
+ importPath: string
326
+ ): string | null {
327
+ const fileDir = dirname(join(sourceDir, relativeFilePath));
328
+ const importBase = join(fileDir, importPath);
329
+ const candidates = [
330
+ importBase,
331
+ `${importBase}.ts`,
332
+ `${importBase}.tsx`,
333
+ join(importBase, "index.ts"),
334
+ join(importBase, "index.tsx"),
335
+ `${importBase}.json`,
336
+ ];
337
+
338
+ for (const candidate of candidates) {
339
+ if (!existsSync(candidate)) {
340
+ continue;
341
+ }
342
+ const stat = statSync(candidate);
343
+ if (!stat.isFile()) {
344
+ continue;
345
+ }
346
+ return relative(sourceDir, candidate).replace(/\\/g, "/");
347
+ }
348
+
349
+ return null;
350
+ }
351
+
291
352
  export function resolveFilesToCopy(sourceDir: string, filesToCopy: string[]): string[] {
292
353
  const resolved = new Set<string>();
293
354
 
@@ -11,27 +11,27 @@ function extractPackageNameFromImportPath(importPath: string): string {
11
11
  return firstSlash === -1 ? importPath : importPath.slice(0, firstSlash);
12
12
  }
13
13
 
14
- export function extractExternalImports(content: string): string[] {
15
- const imports = new Set<string>();
16
- const importRegex =
17
- /import\s+(?:(?:type\s+)?(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
14
+ function extractModuleSpecifiers(content: string): string[] {
15
+ const specifiers = new Set<string>();
16
+ const importOrExportRegex =
17
+ /(?:import|export)\s+(?:(?:type\s+)?(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+)?['"]([^'"]+)['"]/g;
18
18
  const dynamicImportRegex = /import\(\s*['"]([^'"]+)['"]\s*\)/g;
19
19
 
20
20
  let match: RegExpExecArray | null;
21
- while ((match = importRegex.exec(content)) !== null) {
22
- const importPath = match[1];
23
- if (
24
- importPath.startsWith(".") ||
25
- importPath.startsWith("node:") ||
26
- importPath.startsWith("/")
27
- ) {
28
- continue;
29
- }
30
- imports.add(extractPackageNameFromImportPath(importPath));
21
+ while ((match = importOrExportRegex.exec(content)) !== null) {
22
+ specifiers.add(match[1]);
31
23
  }
32
24
 
33
25
  while ((match = dynamicImportRegex.exec(content)) !== null) {
34
- const importPath = match[1];
26
+ specifiers.add(match[1]);
27
+ }
28
+
29
+ return Array.from(specifiers);
30
+ }
31
+
32
+ export function extractExternalImports(content: string): string[] {
33
+ const imports = new Set<string>();
34
+ for (const importPath of extractModuleSpecifiers(content)) {
35
35
  if (
36
36
  importPath.startsWith(".") ||
37
37
  importPath.startsWith("node:") ||
@@ -41,10 +41,15 @@ export function extractExternalImports(content: string): string[] {
41
41
  }
42
42
  imports.add(extractPackageNameFromImportPath(importPath));
43
43
  }
44
-
45
44
  return Array.from(imports);
46
45
  }
47
46
 
47
+ export function extractLocalRelativeImports(content: string): string[] {
48
+ return extractModuleSpecifiers(content).filter(
49
+ (importPath) => importPath.startsWith(".") && !importPath.startsWith("/")
50
+ );
51
+ }
52
+
48
53
  export function extractWorkspaceDependencies(
49
54
  manifest: Record<string, any>
50
55
  ): string[] {
@@ -0,0 +1,32 @@
1
+ export interface DialogLaunchScopeInput {
2
+ routeSpaceId?: string | null;
3
+ recordSpaceId?: string | null;
4
+ currentSpaceId?: string | null;
5
+ viewMode?: string | null;
6
+ allowSidebarSpaceFallback?: boolean;
7
+ }
8
+
9
+ const trimSpaceId = (spaceId?: string | null): string | null => {
10
+ if (typeof spaceId !== "string") return null;
11
+ const trimmed = spaceId.trim();
12
+ return trimmed.length > 0 ? trimmed : null;
13
+ };
14
+
15
+ export const resolveDialogLaunchSpaceId = ({
16
+ routeSpaceId,
17
+ recordSpaceId,
18
+ currentSpaceId,
19
+ viewMode,
20
+ allowSidebarSpaceFallback = false,
21
+ }: DialogLaunchScopeInput): string | null => {
22
+ const explicitSpaceId = trimSpaceId(routeSpaceId) ?? trimSpaceId(recordSpaceId);
23
+ if (explicitSpaceId) {
24
+ return explicitSpaceId;
25
+ }
26
+
27
+ if (!allowSidebarSpaceFallback || viewMode !== "categories") {
28
+ return null;
29
+ }
30
+
31
+ return trimSpaceId(currentSpaceId);
32
+ };