nolo-cli 0.1.39 → 0.1.41
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/README.md +4 -4
- package/agent-runtime/dialogWritePlan.ts +39 -0
- package/agent-runtime/hostAdapter.ts +4 -0
- package/agent-runtime/hybridRecordStore.ts +5 -3
- package/agent-runtime/index.ts +15 -0
- package/agent-runtime/localLoop.ts +87 -7
- package/agent-runtime/localToolPolicy.ts +2 -13
- package/agent-runtime/localWorkspaceTools.ts +42 -110
- package/agent-runtime/runtimeToolPolicy.ts +2 -11
- package/agent-runtime/runtimeToolSurface.ts +198 -0
- package/agent-runtime/types.ts +1 -0
- package/agentAliases.ts +2 -1
- package/agentRunCommand.ts +10 -32
- package/agentRuntimeLocal.ts +1 -0
- package/ai/agent/_executeModel.ts +146 -0
- package/ai/agent/agentSlice.ts +556 -1
- package/ai/agent/appWorkingMemory.ts +126 -0
- package/ai/agent/buildEditingContext.ts +378 -0
- package/ai/agent/buildSystemPrompt.ts +585 -0
- package/ai/agent/canvasEditingContext.ts +49 -0
- package/ai/agent/cleanAgentMessages.ts +140 -0
- package/ai/agent/cliChatClient.ts +153 -0
- package/ai/agent/cliExecutor.ts +314 -40
- package/ai/agent/contextCompiler.ts +107 -0
- package/ai/agent/contextLayerContract.ts +44 -0
- package/ai/agent/createAgentSchema.ts +272 -0
- package/ai/agent/currentTimeContext.ts +65 -0
- package/ai/agent/executeToolCall.ts +58 -0
- package/ai/agent/fetchAgentContexts.ts +42 -0
- package/ai/agent/generatePrompt.ts +3 -0
- package/ai/agent/getFullChatContextKeys.ts +168 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/isLiveAudioOnlyAgent.ts +26 -0
- package/ai/agent/liveAudioModel.ts +2 -0
- package/ai/agent/pageBuilderHandoffRules.ts +75 -0
- package/ai/agent/persistMessageWithFixedId.ts +37 -0
- package/ai/agent/planSlice.ts +259 -0
- package/ai/agent/referenceUtils.ts +229 -0
- package/ai/agent/runAgentBackground.ts +310 -0
- package/ai/agent/runAgentClientLoop.ts +164 -0
- package/ai/agent/runtimeGuidance.ts +116 -0
- package/ai/agent/serverOrigin.ts +7 -0
- package/ai/agent/startParallelAgentStreams.ts +424 -0
- package/ai/agent/startupProtocol.ts +53 -0
- package/ai/agent/streamAgentChatTurn.ts +2001 -0
- package/ai/agent/streamAgentChatTurnUtils.ts +860 -0
- package/ai/agent/toolFailureGuard.ts +99 -0
- package/ai/agent/types.ts +73 -0
- package/ai/agent/utils/imageOutput.ts +37 -0
- package/ai/agent/utils/publicImageAgentMode.ts +26 -0
- package/ai/ai.locale.ts +1095 -0
- package/ai/chat/accumulateToolCallChunks.ts +126 -0
- package/ai/chat/fetchUtils.ts +143 -0
- package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
- package/ai/chat/parseApiError.ts +67 -0
- package/ai/chat/parseMultilineSSE.ts +95 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +897 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +720 -0
- package/ai/chat/serverProxyRetry.ts +102 -0
- package/ai/chat/shouldUseServerProxy.ts +18 -0
- package/ai/chat/streamRetry.ts +46 -0
- package/ai/chat/updateTotalUsage.ts +89 -0
- package/ai/context/buildReferenceContext.ts +437 -0
- package/ai/context/retention.ts +165 -0
- package/ai/context/tokenUtils.ts +78 -0
- package/ai/llm/agentCapabilities.ts +74 -0
- package/ai/llm/deepinfra.ts +79 -0
- package/ai/llm/fireworks.ts +68 -0
- package/ai/llm/generateRequestBody.ts +165 -0
- package/ai/llm/getModelContextWindow.ts +93 -0
- package/ai/llm/getPricing.ts +252 -0
- package/ai/llm/imagePricing.ts +66 -0
- package/ai/llm/isResponseAPIModel.ts +13 -0
- package/ai/llm/kimi.ts +23 -0
- package/ai/llm/mimo.ts +99 -0
- package/ai/llm/mistral.ts +21 -0
- package/ai/llm/models.ts +45 -0
- package/ai/llm/openrouterModels.ts +6 -0
- package/ai/llm/providers.ts +313 -0
- package/ai/llm/reasoningModels.ts +27 -0
- package/ai/llm/types.ts +82 -0
- package/ai/llm/usageRequestOptions.ts +59 -0
- package/ai/memory/queryShared.ts +161 -0
- package/ai/memory/recentRelationshipRecap.ts +247 -0
- package/ai/memory/remember.ts +167 -0
- package/ai/memory/store.ts +20 -0
- package/ai/memory/storeShared.ts +76 -0
- package/ai/memory/types.ts +46 -0
- package/ai/memory/understanding.ts +349 -0
- package/ai/memory/understandingGreeting.ts +264 -0
- package/ai/policy/personalizationDialog.ts +333 -0
- package/ai/policy/runtimePolicy.ts +454 -0
- package/ai/policy/selfUpdateFields.ts +48 -0
- package/ai/policy/types.ts +64 -0
- package/ai/skills/referenceRuntime.ts +274 -0
- package/ai/skills/skillDiagnostics.ts +251 -0
- package/ai/skills/skillDocBuilder.ts +139 -0
- package/ai/skills/skillSummaryMarker.ts +26 -0
- package/ai/token/calculatePrice.ts +576 -0
- package/ai/token/missingUsageEstimate.ts +42 -0
- package/ai/token/normalizeUsage.ts +124 -0
- package/ai/token/openaiImageGenerationUsage.ts +56 -0
- package/ai/token/prepareTokenUsageData.ts +93 -0
- package/ai/token/resolveBillingTarget.ts +52 -0
- package/ai/token/saveTokenRecord.ts +53 -0
- package/ai/token/types.ts +103 -0
- package/ai/tools/agent/agentTools.ts +235 -0
- package/ai/tools/agent/agentUpdateShared.ts +311 -0
- package/ai/tools/agent/callAgentTool.ts +139 -0
- package/ai/tools/agent/createAgentTool.ts +512 -0
- package/ai/tools/agent/createDialogTool.ts +69 -0
- package/ai/tools/agent/createSkillAgentTool.ts +62 -0
- package/ai/tools/agent/parallelBudget.ts +221 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +154 -0
- package/ai/tools/agent/runLlmTool.ts +96 -0
- package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
- package/ai/tools/agent/skillAgentArgs.ts +106 -0
- package/ai/tools/agent/startAgentDialogTool.ts +53 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +123 -0
- package/ai/tools/agent/updateAgentTool.ts +96 -0
- package/ai/tools/agent/updateSelfTool.ts +113 -0
- package/ai/tools/amazonProductScraperTool.ts +86 -0
- package/ai/tools/apifyActorClient.ts +45 -0
- package/ai/tools/appEditGuard.ts +372 -0
- package/ai/tools/appReadSnapshot.ts +153 -0
- package/ai/tools/appTools.ts +1549 -0
- package/ai/tools/applyEditTool.ts +259 -0
- package/ai/tools/applyLineEditsTool.ts +315 -0
- package/ai/tools/browserTools/click.ts +33 -0
- package/ai/tools/browserTools/closeSession.ts +29 -0
- package/ai/tools/browserTools/common.ts +27 -0
- package/ai/tools/browserTools/openSession.ts +48 -0
- package/ai/tools/browserTools/readContent.ts +38 -0
- package/ai/tools/browserTools/selectOption.ts +46 -0
- package/ai/tools/browserTools/typeText.ts +42 -0
- package/ai/tools/category/createCategoryTool.ts +66 -0
- package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
- package/ai/tools/category/updateContentCategoryTool.ts +75 -0
- package/ai/tools/cfBrowserTools.ts +319 -0
- package/ai/tools/cfSpeechToTextTool.ts +49 -0
- package/ai/tools/checkEnvTool.ts +102 -0
- package/ai/tools/cloudflareCrawlTool.ts +289 -0
- package/ai/tools/codeSearchTool.ts +123 -0
- package/ai/tools/codeTools.ts +101 -0
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/createDocTool.ts +132 -0
- package/ai/tools/createSkillDocTool.ts +155 -0
- package/ai/tools/createWorkflowTool.ts +154 -0
- package/ai/tools/delayTool.ts +31 -0
- package/ai/tools/deleteSpacesTool.ts +325 -0
- package/ai/tools/deleteSpacesToolModel.ts +159 -0
- package/ai/tools/devReloadUtils.ts +29 -0
- package/ai/tools/dialogMessageSearch.ts +137 -0
- package/ai/tools/doctorSkillTool.ts +72 -0
- package/ai/tools/ecommerceScraperTool.ts +86 -0
- package/ai/tools/emailTools.ts +549 -0
- package/ai/tools/evalSkillTool.ts +92 -0
- package/ai/tools/exaSearchTool.ts +63 -0
- package/ai/tools/execShellTool.ts +266 -0
- package/ai/tools/executeSqlTool.ts +192 -0
- package/ai/tools/fetchWebpageSupport.ts +333 -0
- package/ai/tools/fetchWebpageTool.ts +97 -0
- package/ai/tools/geminiImagePreviewTool.ts +386 -0
- package/ai/tools/generateDocxTool.ts +215 -0
- package/ai/tools/googleSearchScraperTool.ts +106 -0
- package/ai/tools/importDataTool.ts +133 -0
- package/ai/tools/importSkillTool.ts +162 -0
- package/ai/tools/index.ts +2308 -0
- package/ai/tools/jdProductScraperTool.ts +821 -0
- package/ai/tools/listUserSpacesTool.ts +113 -0
- package/ai/tools/localFilesTool.ts +276 -0
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +206 -0
- package/ai/tools/noloWorkspaceReadTools.ts +467 -0
- package/ai/tools/olmOcrTool.ts +33 -0
- package/ai/tools/openaiImageTool.ts +267 -0
- package/ai/tools/prepareTools.ts +23 -0
- package/ai/tools/readDocTool.ts +88 -0
- package/ai/tools/readFileTool.ts +214 -0
- package/ai/tools/readTool.ts +194 -0
- package/ai/tools/readXPostTool.ts +229 -0
- package/ai/tools/readXhsProfileTool.ts +627 -0
- package/ai/tools/rememberMemoryTool.ts +84 -0
- package/ai/tools/remotionVideoTool.ts +151 -0
- package/ai/tools/searchDialogMessagesTool.ts +222 -0
- package/ai/tools/searchWorkspaceTool.ts +259 -0
- package/ai/tools/skillFollowup.ts +86 -0
- package/ai/tools/surfWeatherTool.ts +169 -0
- package/ai/tools/table/addTableRowTool.ts +217 -0
- package/ai/tools/table/createTableTool.ts +315 -0
- package/ai/tools/table/rowTools.ts +386 -0
- package/ai/tools/table/schemaTools.ts +244 -0
- package/ai/tools/table/shareTableTool.ts +148 -0
- package/ai/tools/table/toolShared.ts +129 -0
- package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
- package/ai/tools/toolApiClient.ts +279 -0
- package/ai/tools/toolResultError.ts +42 -0
- package/ai/tools/toolRunSlice.ts +303 -0
- package/ai/tools/toolSchemaCompatibility.ts +53 -0
- package/ai/tools/uiAskChoiceTool.ts +104 -0
- package/ai/tools/updateContentTitleTool.ts +84 -0
- package/ai/tools/updateDocTool.ts +105 -0
- package/ai/tools/updateUserPreferenceProfileTool.ts +153 -0
- package/ai/tools/wereadGatewayTool.ts +152 -0
- package/ai/tools/whisperTool.ts +76 -0
- package/ai/tools/writeFileTool.ts +213 -0
- package/ai/tools/youtubeScraperTool.ts +116 -0
- package/ai/tools/ziweiChartTool.ts +678 -0
- package/ai/types.ts +55 -0
- package/ai/workflow/workflowExecutor.ts +323 -0
- package/ai/workflow/workflowSlice.ts +73 -0
- package/ai/workflow/workflowTypes.ts +106 -0
- package/app/actions/syncAppRecord.ts +73 -0
- package/app/constants/animationSets.ts +21 -0
- package/app/constants/appEditor.ts +99 -0
- package/app/favorite/favoriteSlice.ts +690 -0
- package/app/favorite/type.ts +43 -0
- package/app/hooks/deleteDbKey.ts +233 -0
- package/app/i18n/client.ts +13 -0
- package/app/i18n/i18n.base.ts +14 -0
- package/app/i18n/i18n.config.ts +77 -0
- package/app/i18n/index.ts +15 -0
- package/app/i18n/mapLanguage.ts +11 -0
- package/app/i18n/translations/appBuilder.locale.ts +453 -0
- package/app/i18n/translations/error.locale.ts +246 -0
- package/app/i18n/translations/interface.locale.ts +2174 -0
- package/app/i18n/translations/pricing.locale.ts +397 -0
- package/app/i18n/translations/privacy.locale.ts +168 -0
- package/app/i18n/translations/seo.locale.ts +129 -0
- package/app/i18n/translations/terms.locale.ts +124 -0
- package/app/i18n/types.ts +6 -0
- package/app/notifications/model.ts +77 -0
- package/app/notifications/notificationSlice.ts +141 -0
- package/app/reducer.ts +36 -0
- package/app/settings/dbActionThunks.ts +34 -0
- package/app/settings/defaultSpacePreference.ts +87 -0
- package/app/settings/settingSlice.tsx +1212 -0
- package/app/settings/settingsRecord.ts +8 -0
- package/app/stateViews/runtime.ts +90 -0
- package/app/store.ts +55 -0
- package/app/theme/colors.ts +280 -0
- package/app/theme/fontPreference.ts +77 -0
- package/app/theme/theme.config.ts +120 -0
- package/app/theme/themeModeBootstrap.ts +123 -0
- package/app/types/appSummary.ts +114 -0
- package/app/types.ts +358 -0
- package/app/utils/appKeys.ts +55 -0
- package/app/utils/async.ts +28 -0
- package/app/utils/desktopAgentRuntimeTurnClient.ts +73 -0
- package/app/utils/env.ts +17 -0
- package/app/utils/fileUtils.ts +229 -0
- package/app/utils/imageUtils.ts +274 -0
- package/app/utils/myContentItems.ts +289 -0
- package/app/utils/retryAfter.ts +36 -0
- package/app/utils/retryFetch.ts +61 -0
- package/app/utils/toast.ts +3 -0
- package/auth/action/signUpAction.ts +227 -0
- package/auth/adminPermissions.ts +18 -0
- package/auth/authSlice.ts +531 -0
- package/auth/client/loginRequest.ts +14 -0
- package/auth/resetAuthScopedClientState.ts +27 -0
- package/auth/routes.ts +178 -0
- package/auth/token.ts +63 -0
- package/auth/types.ts +40 -0
- package/chat/chat.locale.ts +813 -0
- package/chat/dialog/actions/addCybotAction.ts +52 -0
- package/chat/dialog/actions/addReferenceKeysAction.ts +62 -0
- package/chat/dialog/actions/builtinDialogLlm.ts +35 -0
- package/chat/dialog/actions/cleanupCliSession.ts +40 -0
- package/chat/dialog/actions/createDialogAction.ts +379 -0
- package/chat/dialog/actions/createScheduledTaskAction.ts +78 -0
- package/chat/dialog/actions/handleSendMessageAction.ts +197 -0
- package/chat/dialog/actions/handleSendMessageResolver.ts +27 -0
- package/chat/dialog/actions/removeCybotAction.ts +6 -0
- package/chat/dialog/actions/removeDialogAgentAction.ts +44 -0
- package/chat/dialog/actions/replacePrimaryCybotAction.ts +6 -0
- package/chat/dialog/actions/setPrimaryDialogAgentAction.ts +52 -0
- package/chat/dialog/actions/updateDialogSummaryAction.ts +430 -0
- package/chat/dialog/actions/updateDialogTitleAction.ts +151 -0
- package/chat/dialog/actions/updateDialogTitlePolicy.ts +30 -0
- package/chat/dialog/actions/updateTokensAction.ts +233 -0
- package/chat/dialog/dialogAgents.ts +72 -0
- package/chat/dialog/dialogSlice.ts +872 -0
- package/chat/dialog/dialogTitle.ts +81 -0
- package/chat/dialog/dialogTokenStats.ts +12 -0
- package/chat/dialog/dialogUrl.ts +6 -0
- package/chat/dialog/goal.ts +108 -0
- package/chat/dialog/objectAssistantRegistry.ts +286 -0
- package/chat/dialog/schedule.ts +17 -0
- package/chat/messages/fetchAndCacheMessages.ts +119 -0
- package/chat/messages/fetchConvMsgs.ts +51 -0
- package/chat/messages/fileUrl.ts +13 -0
- package/chat/messages/messageAgent.ts +8 -0
- package/chat/messages/messageContent.ts +199 -0
- package/chat/messages/messageContract.ts +88 -0
- package/chat/messages/messageSlice.ts +1367 -0
- package/chat/messages/toolThunks.ts +625 -0
- package/cli/agentAliases.ts +2 -1
- package/cli/agentRunCommand.ts +10 -32
- package/cli/agentRuntimeLocal.ts +1 -0
- package/cli/client/agentRun.ts +67 -95
- package/cli/client/localRuntimeAdapter.ts +323 -54
- package/cli/commandRegistry.ts +2 -2
- package/cli/machineWsRunDispatch.ts +51 -6
- package/cli/offlineMarxistsAgentCommand.ts +6 -4
- package/client/agentConfigResolver.test.ts +2 -2
- package/client/agentRun.test.ts +175 -57
- package/client/agentRun.ts +67 -95
- package/client/localRuntimeAdapter.test.ts +682 -37
- package/client/localRuntimeAdapter.ts +323 -54
- package/client/localRuntimeDryRun.test.ts +23 -9
- package/client/localToolPolicy.test.ts +4 -4
- package/commandRegistry.ts +2 -2
- package/core/builtinAgents.ts +209 -0
- package/core/config.ts +15 -0
- package/core/crypto.ts +51 -0
- package/core/generateKeyPairFromSeedV1.ts +15 -0
- package/core/generateMainKey.ts +22 -0
- package/core/init.ts +41 -0
- package/core/localOrigins.ts +8 -0
- package/core/password.ts +13 -0
- package/core/prefix.ts +23 -0
- package/create/editor/transforms/fromMarkdown/index.ts +260 -0
- package/create/editor/transforms/fromMarkdown/inline.ts +285 -0
- package/create/editor/transforms/fromMarkdown/table.ts +130 -0
- package/create/editor/transforms/markdownToSlate.ts +24 -0
- package/create/editor/transforms/slateToRenderMarkdown.ts +218 -0
- package/create/editor/transforms/slateToSimplifiedMarkdown.ts +224 -0
- package/create/editor/transforms/slateToText.ts +33 -0
- package/create/editor/utils/slateUtils.ts +233 -0
- package/create/space/addSpaceAction.ts +193 -0
- package/create/space/category/categoryActions.ts +427 -0
- package/create/space/changeSpaceAction.ts +36 -0
- package/create/space/constants.ts +8 -0
- package/create/space/content/addContentAction.ts +116 -0
- package/create/space/content/contentThunks.ts +90 -0
- package/create/space/content/deleteContentFromSpaceAction.ts +187 -0
- package/create/space/content/deleteMultipleContentAction.ts +112 -0
- package/create/space/content/moveContentAction.ts +285 -0
- package/create/space/content/updateContentCategoryAction.ts +154 -0
- package/create/space/content/updateContentTitleAction.ts +151 -0
- package/create/space/content/uploadAndAddFileToSpaceAction.ts +98 -0
- package/create/space/contentKeyUtils.ts +117 -0
- package/create/space/deleteSpaceAction.ts +276 -0
- package/create/space/fetchSpaceAction.ts +70 -0
- package/create/space/fetchSpaceSidebarStateAction.ts +26 -0
- package/create/space/loadDefaultSpaceAction.ts +57 -0
- package/create/space/member/addMemberAction.ts +79 -0
- package/create/space/member/fetchUserSpaceMembershipsAction.ts +386 -0
- package/create/space/member/memberThunks.ts +47 -0
- package/create/space/member/removeMemberAction.ts +67 -0
- package/create/space/resolvePreferredSpaceId.ts +113 -0
- package/create/space/sidebarVisibleTypes.ts +241 -0
- package/create/space/space.locale.ts +1087 -0
- package/create/space/spaceAccess.ts +182 -0
- package/create/space/spaceCollapsedState.ts +54 -0
- package/create/space/spaceKeys.ts +65 -0
- package/create/space/spaceSlice.ts +328 -0
- package/create/space/spaceThunks.ts +410 -0
- package/create/space/types.ts +27 -0
- package/create/space/updateSpaceAction.ts +107 -0
- package/create/space/utils/permissions.ts +47 -0
- package/create/types.ts +21 -0
- package/database/actions/agentReadResolution.ts +33 -0
- package/database/actions/common.ts +243 -0
- package/database/actions/deleteFile.ts +40 -0
- package/database/actions/fileContent.ts +125 -0
- package/database/actions/patch.ts +155 -0
- package/database/actions/read.ts +358 -0
- package/database/actions/readAndWait.ts +260 -0
- package/database/actions/readRequestManager.ts +120 -0
- package/database/actions/readResolution.ts +125 -0
- package/database/actions/remove.ts +94 -0
- package/database/actions/replication.ts +477 -0
- package/database/actions/upload.ts +186 -0
- package/database/actions/upsert.ts +56 -0
- package/database/actions/write.ts +146 -0
- package/database/authority/ownerKey.ts +80 -0
- package/database/authority/recordAuthority.ts +67 -0
- package/database/authority/userAuthorityRegistry.ts +54 -0
- package/database/client/fetchUserData.ts +61 -0
- package/database/dbActionThunks.ts +1 -0
- package/database/dbSlice.ts +149 -0
- package/database/fileRing.ts +51 -0
- package/database/fileStorage.ts +232 -0
- package/database/fileUrl.ts +34 -0
- package/database/keys.ts +767 -0
- package/database/queryPrefixes.ts +14 -0
- package/database/requests.ts +443 -0
- package/database/runtimeServerContext.ts +54 -0
- package/database/server/levelAuthorityStore.ts +20 -1
- package/database/tenantPlacement.ts +57 -0
- package/database/userPreferenceRegister.ts +131 -0
- package/database/utils/ulid.ts +1 -0
- package/integrations/anthropic/anthropicModels.ts +47 -0
- package/integrations/deepseek/models.ts +37 -0
- package/integrations/google/models.ts +341 -0
- package/integrations/openai/chatCompletionStreamMode.ts +10 -0
- package/integrations/openai/filterAndCleanMessages.ts +284 -0
- package/integrations/openai/flexTier.ts +102 -0
- package/integrations/openai/generateOpenAIRequestBody.ts +274 -0
- package/integrations/openai/generateResponseRequestBody.ts +77 -0
- package/integrations/openai/models.ts +271 -0
- package/integrations/openai/providerBodyCompatibility.ts +23 -0
- package/integrations/openai/responsesHelpers.ts +297 -0
- package/integrations/x-reader/types.ts +109 -0
- package/integrations/xai/models.ts +13 -0
- package/integrations/xhs-reader/analyze/profileAnalyzer.ts +205 -0
- package/integrations/xhs-reader/backends/playwrightProfileCollector.ts +624 -0
- package/integrations/xhs-reader/bridge/readXhsProfileWithBridge.ts +227 -0
- package/integrations/xhs-reader/client/xhsWebApiClient.ts +229 -0
- package/integrations/xhs-reader/normalize.ts +390 -0
- package/integrations/xhs-reader/orchestrator.ts +178 -0
- package/integrations/xhs-reader/redaction.ts +68 -0
- package/integrations/xhs-reader/types.ts +244 -0
- package/integrations/xhs-reader/url.ts +57 -0
- package/machineWsRunDispatch.ts +51 -6
- package/offlineMarxistsAgentCommand.ts +6 -4
- package/package.json +430 -3
- package/render/page/createPageAction.ts +183 -0
- package/render/page/docSlice.ts +468 -0
- package/render/page/types.ts +17 -0
- package/render/table/createTableAction.ts +270 -0
- package/render/table/deleteTableAction.ts +193 -0
- package/render/table/fetchAndCacheTableRows.ts +174 -0
- package/render/table/tableSlice.ts +1106 -0
- package/render/table/toolValueUtils.ts +363 -0
- package/render/table/types.ts +252 -0
- package/render/table/utils/tableSerialization.ts +50 -0
- package/server/handlers/agentRun/types.ts +104 -0
- package/share/action.ts +225 -0
- package/share/helpers.ts +302 -0
- package/share/keys.ts +243 -0
- package/share/publicSharePolicy.ts +27 -0
- package/share/shareSlice.ts +53 -0
- package/share/types.ts +100 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentRuntimeMessageContent,
|
|
3
|
+
LocalAgentTurnResult,
|
|
4
|
+
} from "../../agent-runtime";
|
|
5
|
+
|
|
6
|
+
export type DesktopAgentRuntimeTurnResult =
|
|
7
|
+
| {
|
|
8
|
+
ok: true;
|
|
9
|
+
result: LocalAgentTurnResult;
|
|
10
|
+
}
|
|
11
|
+
| {
|
|
12
|
+
ok: false;
|
|
13
|
+
error: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type RunDesktopAgentRuntimeTurnArgs = {
|
|
17
|
+
agentRef: string;
|
|
18
|
+
input: AgentRuntimeMessageContent;
|
|
19
|
+
continueDialogId?: string;
|
|
20
|
+
cwd?: string;
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function buildDesktopAgentRuntimeTurnBody(args: RunDesktopAgentRuntimeTurnArgs) {
|
|
25
|
+
const continueDialogId =
|
|
26
|
+
typeof args.continueDialogId === "string"
|
|
27
|
+
? args.continueDialogId.trim()
|
|
28
|
+
: "";
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
agentRef: args.agentRef,
|
|
32
|
+
input: args.input,
|
|
33
|
+
...(continueDialogId ? { continueDialogId } : {}),
|
|
34
|
+
...(args.cwd ? { cwd: args.cwd } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function normalizeDesktopAgentRuntimeTurnError(data: any) {
|
|
39
|
+
return typeof data?.error === "string"
|
|
40
|
+
? data.error
|
|
41
|
+
: "Failed to run desktop agent runtime turn";
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function runDesktopAgentRuntimeTurn({
|
|
45
|
+
fetchImpl = fetch,
|
|
46
|
+
...args
|
|
47
|
+
}: RunDesktopAgentRuntimeTurnArgs): Promise<DesktopAgentRuntimeTurnResult> {
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetchImpl("/api/desktop/agent-runtime/turn", {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "Content-Type": "application/json" },
|
|
52
|
+
body: JSON.stringify(buildDesktopAgentRuntimeTurnBody(args)),
|
|
53
|
+
});
|
|
54
|
+
const data = await response.json().catch(() => ({}));
|
|
55
|
+
if (!response.ok || data?.ok === false) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
error: normalizeDesktopAgentRuntimeTurnError(data),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
result: data.result as LocalAgentTurnResult,
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
error: error instanceof Error
|
|
69
|
+
? error.message
|
|
70
|
+
: "Failed to run desktop agent runtime turn",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
package/app/utils/env.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// app/utils/fileUtils.ts
|
|
2
|
+
export const isBrowser = typeof window !== "undefined";
|
|
3
|
+
const processEnv = typeof process !== "undefined" ? process.env : undefined;
|
|
4
|
+
const reactNativeDev =
|
|
5
|
+
typeof globalThis !== "undefined" && "__DEV__" in globalThis
|
|
6
|
+
? (globalThis as typeof globalThis & { __DEV__?: boolean }).__DEV__
|
|
7
|
+
: undefined;
|
|
8
|
+
|
|
9
|
+
export const isProduction =
|
|
10
|
+
processEnv?.NOLO_FORCE_PRODUCTION === "1" ||
|
|
11
|
+
processEnv?.NODE_ENV === "production" ||
|
|
12
|
+
reactNativeDev === false;
|
|
13
|
+
export const isDevelopment = !isProduction;
|
|
14
|
+
export const getIsDesktopApp = (): boolean =>
|
|
15
|
+
(typeof process !== "undefined" ? process.env?.NOLO_DESKTOP : processEnv?.NOLO_DESKTOP) === "1" ||
|
|
16
|
+
(typeof window !== "undefined" && window.__NOLO_DESKTOP__ === true);
|
|
17
|
+
export const isDesktopApp = getIsDesktopApp();
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// 文件路径: app/utils/fileUtils.ts
|
|
2
|
+
|
|
3
|
+
import type { FileCategory } from "../types";
|
|
4
|
+
|
|
5
|
+
const fileConstructor =
|
|
6
|
+
typeof globalThis !== "undefined" && typeof globalThis.File === "function"
|
|
7
|
+
? globalThis.File
|
|
8
|
+
: undefined;
|
|
9
|
+
|
|
10
|
+
export const isImageMimeType = (value: unknown): value is string =>
|
|
11
|
+
typeof value === "string" && value.toLowerCase().startsWith("image/");
|
|
12
|
+
|
|
13
|
+
const IMAGE_FILE_NAME_RE = /\.(avif|bmp|gif|heic|heif|ico|jpe?g|png|svg|tiff?|webp)$/i;
|
|
14
|
+
const VIDEO_FILE_NAME_RE = /\.(avi|m4v|mkv|mov|mp4|mpeg|mpg|webm)$/i;
|
|
15
|
+
const AUDIO_FILE_NAME_RE = /\.(aac|flac|m4a|mp3|ogg|wav|weba)$/i;
|
|
16
|
+
const DOCUMENT_FILE_NAME_RE =
|
|
17
|
+
/\.(csv|doc|docx|md|odt|pdf|ppt|pptx|rtf|txt|xls|xlsx)$/i;
|
|
18
|
+
|
|
19
|
+
export const isImageFileName = (value: unknown): value is string =>
|
|
20
|
+
typeof value === "string" && IMAGE_FILE_NAME_RE.test(value.trim());
|
|
21
|
+
|
|
22
|
+
export const formatFileSize = (bytes: number): string => {
|
|
23
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "";
|
|
24
|
+
if (bytes === 0) return "0 B";
|
|
25
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
26
|
+
const unitIndex = Math.min(
|
|
27
|
+
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
28
|
+
units.length - 1
|
|
29
|
+
);
|
|
30
|
+
const value = bytes / Math.pow(1024, unitIndex);
|
|
31
|
+
const digits = value >= 10 || unitIndex === 0 ? 0 : 1;
|
|
32
|
+
return `${value.toFixed(digits)} ${units[unitIndex]}`;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const MIME_FORMAT_LABELS: Record<string, string> = {
|
|
36
|
+
"application/pdf": "PDF",
|
|
37
|
+
"application/msword": "DOC",
|
|
38
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX",
|
|
39
|
+
"application/vnd.ms-powerpoint": "PPT",
|
|
40
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "PPTX",
|
|
41
|
+
"application/vnd.ms-excel": "XLS",
|
|
42
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "XLSX",
|
|
43
|
+
"text/plain": "TXT",
|
|
44
|
+
"text/markdown": "MD",
|
|
45
|
+
"image/jpeg": "JPG",
|
|
46
|
+
"image/png": "PNG",
|
|
47
|
+
"image/webp": "WEBP",
|
|
48
|
+
"image/gif": "GIF",
|
|
49
|
+
"video/mp4": "MP4",
|
|
50
|
+
"video/quicktime": "MOV",
|
|
51
|
+
"audio/mpeg": "MP3",
|
|
52
|
+
"audio/wav": "WAV",
|
|
53
|
+
"audio/x-wav": "WAV",
|
|
54
|
+
"audio/mp4": "M4A",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const resolveFileFormatLabel = ({
|
|
58
|
+
fileName,
|
|
59
|
+
mimeType,
|
|
60
|
+
}: {
|
|
61
|
+
fileName?: unknown;
|
|
62
|
+
mimeType?: unknown;
|
|
63
|
+
}): string | null => {
|
|
64
|
+
if (typeof fileName === "string") {
|
|
65
|
+
const normalizedName = fileName.trim();
|
|
66
|
+
const ext = normalizedName.includes(".")
|
|
67
|
+
? normalizedName.split(".").pop()?.trim().toUpperCase()
|
|
68
|
+
: "";
|
|
69
|
+
if (ext) return ext;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof mimeType === "string") {
|
|
73
|
+
const normalizedMimeType = mimeType.trim().toLowerCase();
|
|
74
|
+
if (MIME_FORMAT_LABELS[normalizedMimeType]) {
|
|
75
|
+
return MIME_FORMAT_LABELS[normalizedMimeType];
|
|
76
|
+
}
|
|
77
|
+
const [, subtype] = normalizedMimeType.split("/");
|
|
78
|
+
if (subtype) {
|
|
79
|
+
return subtype.split("+")[0]?.split(".").pop()?.toUpperCase() ?? null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const getCompactFileMetaLabel = ({
|
|
87
|
+
fileName,
|
|
88
|
+
mimeType,
|
|
89
|
+
fileSize,
|
|
90
|
+
}: {
|
|
91
|
+
fileName?: unknown;
|
|
92
|
+
mimeType?: unknown;
|
|
93
|
+
fileSize?: unknown;
|
|
94
|
+
}): string | null => {
|
|
95
|
+
const formatLabel = resolveFileFormatLabel({ fileName, mimeType });
|
|
96
|
+
const sizeLabel =
|
|
97
|
+
typeof fileSize === "number" && Number.isFinite(fileSize)
|
|
98
|
+
? formatFileSize(fileSize)
|
|
99
|
+
: null;
|
|
100
|
+
|
|
101
|
+
if (formatLabel && sizeLabel) return `${formatLabel} · ${sizeLabel}`;
|
|
102
|
+
return formatLabel ?? sizeLabel;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const isVideoMimeType = (value: unknown): value is string =>
|
|
106
|
+
typeof value === "string" && value.toLowerCase().startsWith("video/");
|
|
107
|
+
|
|
108
|
+
export const isAudioMimeType = (value: unknown): value is string =>
|
|
109
|
+
typeof value === "string" && value.toLowerCase().startsWith("audio/");
|
|
110
|
+
|
|
111
|
+
export const isPdfMimeType = (value: unknown): value is string =>
|
|
112
|
+
typeof value === "string" && value.toLowerCase() === "application/pdf";
|
|
113
|
+
|
|
114
|
+
export const isDocumentMimeType = (value: unknown): value is string => {
|
|
115
|
+
if (typeof value !== "string") return false;
|
|
116
|
+
const normalized = value.toLowerCase();
|
|
117
|
+
return (
|
|
118
|
+
isPdfMimeType(normalized) ||
|
|
119
|
+
normalized === "text/plain" ||
|
|
120
|
+
normalized === "text/markdown" ||
|
|
121
|
+
normalized === "text/csv" ||
|
|
122
|
+
normalized === "application/msword" ||
|
|
123
|
+
normalized ===
|
|
124
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document" ||
|
|
125
|
+
normalized === "application/vnd.ms-powerpoint" ||
|
|
126
|
+
normalized ===
|
|
127
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation" ||
|
|
128
|
+
normalized === "application/vnd.ms-excel" ||
|
|
129
|
+
normalized ===
|
|
130
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
|
|
131
|
+
normalized === "application/rtf" ||
|
|
132
|
+
normalized === "application/vnd.oasis.opendocument.text"
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const resolveFileCategory = ({
|
|
137
|
+
mimeType,
|
|
138
|
+
fileName,
|
|
139
|
+
}: {
|
|
140
|
+
mimeType?: unknown;
|
|
141
|
+
fileName?: unknown;
|
|
142
|
+
}): FileCategory => {
|
|
143
|
+
if (isImageMimeType(mimeType) || isImageFileName(fileName)) return "image";
|
|
144
|
+
if (isVideoMimeType(mimeType) || (typeof fileName === "string" && VIDEO_FILE_NAME_RE.test(fileName.trim()))) {
|
|
145
|
+
return "video";
|
|
146
|
+
}
|
|
147
|
+
if (isAudioMimeType(mimeType) || (typeof fileName === "string" && AUDIO_FILE_NAME_RE.test(fileName.trim()))) {
|
|
148
|
+
return "audio";
|
|
149
|
+
}
|
|
150
|
+
if (isDocumentMimeType(mimeType) || (typeof fileName === "string" && DOCUMENT_FILE_NAME_RE.test(fileName.trim()))) {
|
|
151
|
+
return "document";
|
|
152
|
+
}
|
|
153
|
+
return "other";
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const isImageResourceLike = ({
|
|
157
|
+
kind,
|
|
158
|
+
mimeType,
|
|
159
|
+
fileName,
|
|
160
|
+
fileCategory,
|
|
161
|
+
}: {
|
|
162
|
+
kind?: unknown;
|
|
163
|
+
mimeType?: unknown;
|
|
164
|
+
fileName?: unknown;
|
|
165
|
+
fileCategory?: unknown;
|
|
166
|
+
}): boolean => {
|
|
167
|
+
if (fileCategory === "image") return true;
|
|
168
|
+
if (typeof kind === "string") {
|
|
169
|
+
const normalizedKind = kind.toLowerCase();
|
|
170
|
+
if (normalizedKind === "image" || isImageMimeType(normalizedKind)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return isImageMimeType(mimeType) || isImageFileName(fileName);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const isBrowserFile = (value: unknown): value is File =>
|
|
179
|
+
!!fileConstructor && value instanceof fileConstructor;
|
|
180
|
+
|
|
181
|
+
export const isImageFile = (value: unknown): value is File =>
|
|
182
|
+
isBrowserFile(value) && isImageMimeType(value.type);
|
|
183
|
+
|
|
184
|
+
export const filterImageFiles = (values: Iterable<unknown>): File[] =>
|
|
185
|
+
Array.from(values).filter(isImageFile);
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 按是否为图片拆分文件数组:
|
|
189
|
+
* - 第 0 个数组是图片
|
|
190
|
+
* - 第 1 个数组是非图片
|
|
191
|
+
*/
|
|
192
|
+
export function splitFiles(files: File[]): [File[], File[]] {
|
|
193
|
+
return files.reduce(
|
|
194
|
+
(acc, file) => {
|
|
195
|
+
const index = isImageFile(file) ? 0 : 1;
|
|
196
|
+
acc[index].push(file);
|
|
197
|
+
return acc;
|
|
198
|
+
},
|
|
199
|
+
[[], []] as [File[], File[]]
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* 从 DataTransfer 中安全提取文件列表
|
|
205
|
+
* 兼容 dt.items 和 dt.files 两种情况
|
|
206
|
+
*/
|
|
207
|
+
export function extractFilesFromDataTransfer(
|
|
208
|
+
dt: DataTransfer | null
|
|
209
|
+
): File[] {
|
|
210
|
+
if (!dt) return [];
|
|
211
|
+
|
|
212
|
+
const files: File[] = [];
|
|
213
|
+
|
|
214
|
+
if (dt.items && dt.items.length > 0) {
|
|
215
|
+
for (const item of Array.from(dt.items)) {
|
|
216
|
+
if (item.kind === "file") {
|
|
217
|
+
const file = item.getAsFile();
|
|
218
|
+
if (file) files.push(file);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (files.length > 0) return files;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (dt.files && dt.files.length > 0) {
|
|
225
|
+
return Array.from(dt.files);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// 文件路径: utils/imageUtils.ts
|
|
2
|
+
import imageCompression from "browser-image-compression";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 前端图片压缩配置(只包含我们实际用到的字段)。
|
|
6
|
+
* 可以按需扩展字段。
|
|
7
|
+
*/
|
|
8
|
+
export interface ImageCompressionOptions {
|
|
9
|
+
maxSizeMB?: number;
|
|
10
|
+
maxWidthOrHeight?: number;
|
|
11
|
+
useWebWorker?: boolean;
|
|
12
|
+
initialQuality?: number;
|
|
13
|
+
alwaysKeepResolution?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 默认压缩参数:
|
|
18
|
+
* - 体积控制在 ~1.5MB 内
|
|
19
|
+
* - 最长边 1400 像素
|
|
20
|
+
* - 初始质量 0.9(画质较高)
|
|
21
|
+
* - 使用 WebWorker 避免主线程卡顿
|
|
22
|
+
*/
|
|
23
|
+
const DEFAULT_COMPRESSION_OPTIONS: Required<
|
|
24
|
+
Pick<
|
|
25
|
+
ImageCompressionOptions,
|
|
26
|
+
"maxSizeMB" | "maxWidthOrHeight" | "useWebWorker" | "initialQuality"
|
|
27
|
+
>
|
|
28
|
+
> = {
|
|
29
|
+
maxSizeMB: 1.5,
|
|
30
|
+
maxWidthOrHeight: 1400,
|
|
31
|
+
useWebWorker: true,
|
|
32
|
+
initialQuality: 0.9,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const BYTES_PER_MB = 1024 * 1024;
|
|
36
|
+
|
|
37
|
+
const toMegabytes = (bytes: number): number => bytes / BYTES_PER_MB;
|
|
38
|
+
|
|
39
|
+
const sleep = (ms: number): Promise<void> =>
|
|
40
|
+
new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 解析 dataURL,提取 mime 和 base64 数据部分。
|
|
44
|
+
*/
|
|
45
|
+
const parseDataUrl = (
|
|
46
|
+
dataUrl: string
|
|
47
|
+
): { mime: string; base64: string } | null => {
|
|
48
|
+
const trimmed = dataUrl.trim();
|
|
49
|
+
const parts = trimmed.split(",");
|
|
50
|
+
|
|
51
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) {
|
|
52
|
+
console.warn("[imageUtils] parseDataUrl: invalid data URL", {
|
|
53
|
+
hasHeader: !!parts[0],
|
|
54
|
+
hasBody: !!parts[1],
|
|
55
|
+
});
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const header = parts[0];
|
|
60
|
+
const base64 = parts[1];
|
|
61
|
+
|
|
62
|
+
const mimeMatch = header.match(/:(.*?);/);
|
|
63
|
+
const mime = mimeMatch?.[1];
|
|
64
|
+
|
|
65
|
+
if (!mime) {
|
|
66
|
+
console.warn("[imageUtils] parseDataUrl: cannot extract mime from", header);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { mime, base64 };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 将 data URL 字符串转回 File 对象。
|
|
75
|
+
* @param dataUrl 形如 "data:image/png;base64,..." 的字符串
|
|
76
|
+
* @param filename 生成 File 时使用的文件名
|
|
77
|
+
* @returns File 对象,失败时返回 null
|
|
78
|
+
*/
|
|
79
|
+
export function dataURLtoFile(
|
|
80
|
+
dataUrl: string,
|
|
81
|
+
filename: string
|
|
82
|
+
): File | null {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = parseDataUrl(dataUrl);
|
|
85
|
+
if (!parsed) return null;
|
|
86
|
+
|
|
87
|
+
const { mime, base64 } = parsed;
|
|
88
|
+
|
|
89
|
+
const binaryString = atob(base64);
|
|
90
|
+
const length = binaryString.length;
|
|
91
|
+
const u8arr = new Uint8Array(length);
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < length; i++) {
|
|
94
|
+
u8arr[i] = binaryString.charCodeAt(i);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return new File([u8arr], filename, { type: mime });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error("[imageUtils] Error converting data URL to File:", error);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const normalizeCompressedFile = (sourceFile: File, compressed: Blob | File): File => {
|
|
105
|
+
if (compressed instanceof File) {
|
|
106
|
+
return compressed;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return new File([compressed], sourceFile.name, {
|
|
110
|
+
type: compressed.type || sourceFile.type || "application/octet-stream",
|
|
111
|
+
lastModified: sourceFile.lastModified || Date.now(),
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export async function compressImageFile(
|
|
116
|
+
imageFile: File,
|
|
117
|
+
options?: ImageCompressionOptions
|
|
118
|
+
): Promise<File> {
|
|
119
|
+
const mergedOptions: ImageCompressionOptions = {
|
|
120
|
+
...DEFAULT_COMPRESSION_OPTIONS,
|
|
121
|
+
...options,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const originalSizeMB = toMegabytes(imageFile.size);
|
|
125
|
+
const targetSizeMB =
|
|
126
|
+
mergedOptions.maxSizeMB ?? DEFAULT_COMPRESSION_OPTIONS.maxSizeMB;
|
|
127
|
+
|
|
128
|
+
console.log(
|
|
129
|
+
`[imageUtils] compressImageFile: original size = ${originalSizeMB.toFixed(
|
|
130
|
+
2
|
|
131
|
+
)} MB`
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (originalSizeMB <= targetSizeMB) {
|
|
135
|
+
console.log(
|
|
136
|
+
"[imageUtils] compressImageFile: image already smaller than target, skip compression"
|
|
137
|
+
);
|
|
138
|
+
return imageFile;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const compressedBlob = await imageCompression(imageFile, mergedOptions);
|
|
143
|
+
const compressedFile = normalizeCompressedFile(imageFile, compressedBlob);
|
|
144
|
+
const compressedSizeMB = toMegabytes(compressedFile.size);
|
|
145
|
+
|
|
146
|
+
console.log(
|
|
147
|
+
`[imageUtils] compressImageFile: compressed size = ${compressedSizeMB.toFixed(
|
|
148
|
+
2
|
|
149
|
+
)} MB (target <= ${targetSizeMB.toFixed(2)} MB)`
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
if (compressedFile.size >= imageFile.size) {
|
|
153
|
+
console.log(
|
|
154
|
+
"[imageUtils] compressImageFile: compressed file is not smaller, return original"
|
|
155
|
+
);
|
|
156
|
+
return imageFile;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return compressedFile;
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error("[imageUtils] compressImageFile failed:", error);
|
|
162
|
+
return imageFile;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 压缩一张 dataURL 图片。
|
|
168
|
+
* 使用 browser-image-compression 库。
|
|
169
|
+
*
|
|
170
|
+
* @param imageDataUrl 原始图片的 dataURL(Base64)
|
|
171
|
+
* @param options 可选的压缩参数,会覆盖默认值
|
|
172
|
+
* @returns 压缩后的 dataURL;如果压缩失败或不划算,会返回原始 dataURL
|
|
173
|
+
*/
|
|
174
|
+
export async function compressImage(
|
|
175
|
+
imageDataUrl: string,
|
|
176
|
+
options?: ImageCompressionOptions
|
|
177
|
+
): Promise<string> {
|
|
178
|
+
// 为转换生成一个“相对唯一”的文件名
|
|
179
|
+
const timestamp = Date.now();
|
|
180
|
+
const filename = `image_${timestamp}.png`; // 实际类型由 dataURL 决定
|
|
181
|
+
|
|
182
|
+
const imageFile = dataURLtoFile(imageDataUrl, filename);
|
|
183
|
+
|
|
184
|
+
if (!imageFile) {
|
|
185
|
+
console.warn(
|
|
186
|
+
"[imageUtils] compressImage: cannot convert data URL to File, return original"
|
|
187
|
+
);
|
|
188
|
+
return imageDataUrl;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const compressedFile = await compressImageFile(imageFile, options);
|
|
193
|
+
if (compressedFile === imageFile) {
|
|
194
|
+
console.log(
|
|
195
|
+
"[imageUtils] compressImage: file-based compression kept original image"
|
|
196
|
+
);
|
|
197
|
+
return imageDataUrl;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const compressedDataUrl =
|
|
201
|
+
await imageCompression.getDataUrlFromFile(compressedFile);
|
|
202
|
+
|
|
203
|
+
return compressedDataUrl;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error("[imageUtils] compressImage failed:", error);
|
|
206
|
+
return imageDataUrl;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 等待 remote /file/content/:fileId 对应的图片 URL 可用。
|
|
212
|
+
* 通过创建 <img> 去加载这个 URL,避免 CORS 问题。
|
|
213
|
+
*
|
|
214
|
+
* 成功:在 maxWaitMs 内,某次加载 onload 触发。
|
|
215
|
+
* 失败:超时或每次都是 onerror。
|
|
216
|
+
*/
|
|
217
|
+
export interface WaitForFileReadyOptions {
|
|
218
|
+
/** 最大等待时间(毫秒),默认 4000ms */
|
|
219
|
+
maxWaitMs?: number;
|
|
220
|
+
/** 每次重试之间的间隔时间(毫秒),默认 250ms */
|
|
221
|
+
intervalMs?: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const appendNoCacheQuery = (url: string): string => {
|
|
225
|
+
const stamp = `_t=${Date.now()}`;
|
|
226
|
+
return url.includes("?") ? `${url}&${stamp}` : `${url}?${stamp}`;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const tryLoadImage = (url: string): Promise<boolean> =>
|
|
230
|
+
new Promise((resolve) => {
|
|
231
|
+
const img = new Image();
|
|
232
|
+
|
|
233
|
+
const cleanup = () => {
|
|
234
|
+
img.onload = null;
|
|
235
|
+
img.onerror = null;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
img.onload = () => {
|
|
239
|
+
cleanup();
|
|
240
|
+
resolve(true);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
img.onerror = () => {
|
|
244
|
+
cleanup();
|
|
245
|
+
resolve(false);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
img.src = url;
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
export const waitForFileReady = async (
|
|
252
|
+
url: string,
|
|
253
|
+
{
|
|
254
|
+
maxWaitMs = 4000,
|
|
255
|
+
intervalMs = 250,
|
|
256
|
+
}: WaitForFileReadyOptions = {}
|
|
257
|
+
): Promise<boolean> => {
|
|
258
|
+
const start = Date.now();
|
|
259
|
+
|
|
260
|
+
while (Date.now() - start < maxWaitMs) {
|
|
261
|
+
const tryUrl = appendNoCacheQuery(url);
|
|
262
|
+
const ok = await tryLoadImage(tryUrl);
|
|
263
|
+
|
|
264
|
+
if (ok) {
|
|
265
|
+
console.debug("[imageUtils] waitForFileReady: image loaded for", url);
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
await sleep(intervalMs);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
console.warn("[imageUtils] waitForFileReady: timeout for", url);
|
|
273
|
+
return false;
|
|
274
|
+
};
|