mirror of
https://github.com/langgenius/dify.git
synced 2026-04-30 15:38:08 +08:00
feat: implement app runtime upgrade feature to clone and convert classic runtime apps to sandboxed mode
This commit is contained in:
@ -7,7 +7,6 @@ import type { CreateAppModalProps } from '@/app/components/explore/create-app-mo
|
||||
import type { EnvironmentVariable } from '@/app/components/workflow/types'
|
||||
import type { WorkflowOnlineUser } from '@/models/app'
|
||||
import type { App } from '@/types/app'
|
||||
import { RiBuildingLine, RiGlobalLine, RiLockLine, RiMoreFill, RiVerifiedBadgeLine } from '@remixicon/react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import * as React from 'react'
|
||||
@ -29,7 +28,7 @@ import { useProviderContext } from '@/context/provider-context'
|
||||
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { useGetUserCanAccessApp } from '@/service/access-control'
|
||||
import { copyApp, deleteApp, exportAppBundle, exportAppConfig, updateAppInfo } from '@/service/apps'
|
||||
import { copyApp, deleteApp, exportAppBundle, exportAppConfig, updateAppInfo, upgradeAppRuntime } from '@/service/apps'
|
||||
import { fetchInstalledAppList } from '@/service/explore'
|
||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
@ -89,10 +88,10 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
onRefresh()
|
||||
onPlanInfoChanged()
|
||||
}
|
||||
catch (e: any) {
|
||||
catch (e: unknown) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: `${t('appDeleteFailed', { ns: 'app' })}${'message' in e ? `: ${e.message}` : ''}`,
|
||||
message: `${t('appDeleteFailed', { ns: 'app' })}${e instanceof Error ? `: ${e.message}` : ''}`,
|
||||
})
|
||||
}
|
||||
setShowConfirmDelete(false)
|
||||
@ -126,10 +125,10 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
if (onRefresh)
|
||||
onRefresh()
|
||||
}
|
||||
catch (e: any) {
|
||||
catch (e: unknown) {
|
||||
notify({
|
||||
type: 'error',
|
||||
message: e.message || t('editFailed', { ns: 'app' }),
|
||||
message: (e instanceof Error ? e.message : '') || t('editFailed', { ns: 'app' }),
|
||||
})
|
||||
}
|
||||
}, [app.id, notify, onRefresh, t])
|
||||
@ -213,6 +212,10 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
setShowSwitchModal(false)
|
||||
}
|
||||
|
||||
const [isUpgradingRuntime, startUpgradeRuntime] = useTransition()
|
||||
const isClassicWorkflowApp = app.runtime_type !== 'sandboxed'
|
||||
&& (app.mode === AppModeEnum.WORKFLOW || app.mode === AppModeEnum.ADVANCED_CHAT)
|
||||
|
||||
const onUpdateAccessControl = useCallback(() => {
|
||||
if (onRefresh)
|
||||
onRefresh()
|
||||
@ -268,8 +271,8 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
e.preventDefault()
|
||||
try {
|
||||
await openAsyncWindow(async () => {
|
||||
const { installed_apps }: any = await fetchInstalledAppList(app.id) || {}
|
||||
if (installed_apps?.length > 0)
|
||||
const { installed_apps } = (await fetchInstalledAppList(app.id) || {}) as { installed_apps?: { id: string }[] }
|
||||
if (installed_apps && installed_apps.length > 0)
|
||||
return `${basePath}/explore/installed/${installed_apps[0].id}`
|
||||
throw new Error('No app found in Explore')
|
||||
}, {
|
||||
@ -278,10 +281,31 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
},
|
||||
})
|
||||
}
|
||||
catch (e: any) {
|
||||
Toast.notify({ type: 'error', message: `${e.message || e}` })
|
||||
catch (e: unknown) {
|
||||
Toast.notify({ type: 'error', message: e instanceof Error ? e.message : String(e) })
|
||||
}
|
||||
}
|
||||
const onClickUpgradeRuntime = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.stopPropagation()
|
||||
props.onClick?.()
|
||||
e.preventDefault()
|
||||
startUpgradeRuntime(async () => {
|
||||
try {
|
||||
const res = await upgradeAppRuntime(app.id)
|
||||
if (res.result === 'success' && res.new_app_id) {
|
||||
notify({ type: 'success', message: t('sandboxMigrationModal.upgrade', { ns: 'workflow' }) })
|
||||
const params = new URLSearchParams({
|
||||
upgraded_from: app.id,
|
||||
upgraded_from_name: app.name,
|
||||
})
|
||||
push(`/app/${res.new_app_id}/workflow?${params.toString()}`)
|
||||
}
|
||||
}
|
||||
catch (e: unknown) {
|
||||
notify({ type: 'error', message: (e instanceof Error ? e.message : '') || 'Upgrade failed' })
|
||||
}
|
||||
})
|
||||
}
|
||||
return (
|
||||
<div className="relative flex w-full flex-col py-1" onMouseLeave={onMouseLeave}>
|
||||
<button type="button" className="mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 hover:bg-state-base-hover" onClick={onClickSettings}>
|
||||
@ -338,6 +362,16 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
</>
|
||||
)
|
||||
}
|
||||
{isClassicWorkflowApp && (
|
||||
<button
|
||||
type="button"
|
||||
disabled={isUpgradingRuntime}
|
||||
className="mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 hover:bg-state-base-hover disabled:cursor-not-allowed disabled:opacity-50"
|
||||
onClick={onClickUpgradeRuntime}
|
||||
>
|
||||
<span className="text-text-accent system-sm-regular">{t('upgradeRuntime', { ns: 'app' })}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="group mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-destructive-hover"
|
||||
@ -410,22 +444,22 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
<div className="flex h-5 w-5 shrink-0 items-center justify-center">
|
||||
{app.access_mode === AccessMode.PUBLIC && (
|
||||
<Tooltip asChild={false} popupContent={t('accessItemsDescription.anyone', { ns: 'app' })}>
|
||||
<RiGlobalLine className="h-4 w-4 text-text-quaternary" />
|
||||
<span className="i-ri-global-line h-4 w-4 text-text-quaternary" />
|
||||
</Tooltip>
|
||||
)}
|
||||
{app.access_mode === AccessMode.SPECIFIC_GROUPS_MEMBERS && (
|
||||
<Tooltip asChild={false} popupContent={t('accessItemsDescription.specific', { ns: 'app' })}>
|
||||
<RiLockLine className="h-4 w-4 text-text-quaternary" />
|
||||
<span className="i-ri-lock-line h-4 w-4 text-text-quaternary" />
|
||||
</Tooltip>
|
||||
)}
|
||||
{app.access_mode === AccessMode.ORGANIZATION && (
|
||||
<Tooltip asChild={false} popupContent={t('accessItemsDescription.organization', { ns: 'app' })}>
|
||||
<RiBuildingLine className="h-4 w-4 text-text-quaternary" />
|
||||
<span className="i-ri-building-line h-4 w-4 text-text-quaternary" />
|
||||
</Tooltip>
|
||||
)}
|
||||
{app.access_mode === AccessMode.EXTERNAL_MEMBERS && (
|
||||
<Tooltip asChild={false} popupContent={t('accessItemsDescription.external', { ns: 'app' })}>
|
||||
<RiVerifiedBadgeLine className="h-4 w-4 text-text-quaternary" />
|
||||
<span className="i-ri-verified-badge-line h-4 w-4 text-text-quaternary" />
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
@ -435,7 +469,7 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="title-wrapper h-[90px] px-[14px] text-xs leading-normal text-text-tertiary">
|
||||
<div className="h-[90px] px-[14px] text-xs leading-normal text-text-tertiary">
|
||||
<div
|
||||
className="line-clamp-2"
|
||||
title={app.description}
|
||||
@ -475,7 +509,7 @@ const AppCard = ({ app, onRefresh, onlineUsers = [] }: AppCardProps) => {
|
||||
<div
|
||||
className="flex h-8 w-8 cursor-pointer items-center justify-center rounded-md"
|
||||
>
|
||||
<RiMoreFill className="h-4 w-4 text-text-tertiary" />
|
||||
<span className="i-ri-more-fill h-4 w-4 text-text-tertiary" />
|
||||
</div>
|
||||
)}
|
||||
btnClassName={open =>
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type Props = {
|
||||
onUpgrade: () => void
|
||||
}
|
||||
|
||||
const UpgradeRuntimeBanner: FC<Props> = ({ onUpgrade }) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const [visible, setVisible] = useState(true)
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setVisible(false)
|
||||
}, [])
|
||||
|
||||
if (!visible)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2 border-b border-divider-subtle bg-components-panel-bg px-4 py-2">
|
||||
<span className="text-text-secondary system-xs-regular">
|
||||
{t('sandboxMigrationModal.bannerHint')}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-0.5 text-text-accent system-xs-medium hover:text-text-accent-secondary"
|
||||
onClick={onUpgrade}
|
||||
>
|
||||
<span className="i-ri-sparkling-line h-3.5 w-3.5" />
|
||||
{t('sandboxMigrationModal.upgrade')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center rounded-md p-0.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<span className="i-ri-close-line h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UpgradeRuntimeBanner
|
||||
@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
type Props = {
|
||||
fromAppName: string
|
||||
fromAppId: string
|
||||
}
|
||||
|
||||
const UpgradedFromBanner: FC<Props> = ({ fromAppName, fromAppId }) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const [visible, setVisible] = useState(true)
|
||||
|
||||
const handleGoBack = useCallback(() => {
|
||||
window.location.href = `/app/${fromAppId}/workflow`
|
||||
}, [fromAppId])
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setVisible(false)
|
||||
}, [])
|
||||
|
||||
if (!visible)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2 border-b border-divider-subtle bg-components-panel-bg px-4 py-2">
|
||||
<span className="text-text-secondary system-xs-regular">
|
||||
{t('sandboxMigrationModal.upgradedFrom', { name: fromAppName })}
|
||||
</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-0.5 text-text-accent system-xs-medium hover:text-text-accent-secondary"
|
||||
onClick={handleGoBack}
|
||||
>
|
||||
<span className="i-ri-arrow-left-line h-3.5 w-3.5" />
|
||||
{t('sandboxMigrationModal.viewOriginal')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center rounded-md p-0.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<span className="i-ri-close-line h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UpgradedFromBanner
|
||||
@ -37,12 +37,15 @@ import {
|
||||
initialNodes,
|
||||
} from '@/app/components/workflow/utils'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { upgradeAppRuntime } from '@/service/apps'
|
||||
import { fetchRunDetail } from '@/service/log'
|
||||
import { useAppTriggers } from '@/service/use-tools'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { useFeatures } from '../base/features/hooks'
|
||||
import ViewPicker from '../workflow/view-picker'
|
||||
import SandboxMigrationModal from './components/sandbox-migration-modal'
|
||||
import UpgradeRuntimeBanner from './components/upgrade-runtime-banner'
|
||||
import UpgradedFromBanner from './components/upgraded-from-banner'
|
||||
import WorkflowAppMain from './components/workflow-main'
|
||||
import { useGetRunAndTraceUrl } from './hooks/use-get-run-and-trace-url'
|
||||
import { useNodesSyncDraft } from './hooks/use-nodes-sync-draft'
|
||||
@ -177,8 +180,8 @@ const WorkflowAppWithAdditionalContext = () => {
|
||||
} = useWorkflowInit()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { isLoadingCurrentWorkspace, currentWorkspace } = useAppContext()
|
||||
const notSupportMigration = true // wait for backend support
|
||||
const [showMigrationModal, setShowMigrationModal] = useState(false)
|
||||
const [isUpgrading, setIsUpgrading] = useState(false)
|
||||
const lastCheckedAppIdRef = useRef<string | null>(null)
|
||||
|
||||
// Initialize trigger status at application level
|
||||
@ -189,6 +192,40 @@ const WorkflowAppWithAdditionalContext = () => {
|
||||
setSandboxMigrationDismissed(appId)
|
||||
setShowMigrationModal(false)
|
||||
}, [appId])
|
||||
|
||||
const handleOpenMigrationModal = useCallback(() => {
|
||||
setShowMigrationModal(true)
|
||||
}, [])
|
||||
|
||||
const showUpgradeRuntimeModal = useStore(s => s.showUpgradeRuntimeModal)
|
||||
const setShowUpgradeRuntimeModal = useStore(s => s.setShowUpgradeRuntimeModal)
|
||||
useEffect(() => {
|
||||
if (showUpgradeRuntimeModal) {
|
||||
// eslint-disable-next-line react-hooks-extra/no-direct-set-state-in-use-effect
|
||||
setShowMigrationModal(true)
|
||||
setShowUpgradeRuntimeModal(false)
|
||||
}
|
||||
}, [showUpgradeRuntimeModal, setShowUpgradeRuntimeModal])
|
||||
|
||||
const handleUpgradeRuntime = useCallback(async () => {
|
||||
if (!appId || isUpgrading)
|
||||
return
|
||||
setIsUpgrading(true)
|
||||
try {
|
||||
const res = await upgradeAppRuntime(appId)
|
||||
if (res.result === 'success' && res.new_app_id) {
|
||||
const appName = appDetail?.name || ''
|
||||
const params = new URLSearchParams({
|
||||
upgraded_from: appId,
|
||||
upgraded_from_name: appName,
|
||||
})
|
||||
window.location.href = `/app/${res.new_app_id}/workflow?${params.toString()}`
|
||||
}
|
||||
}
|
||||
finally {
|
||||
setIsUpgrading(false)
|
||||
}
|
||||
}, [appId, appDetail?.name, isUpgrading])
|
||||
const isWorkflowMode = appDetail?.mode === AppModeEnum.WORKFLOW
|
||||
const { data: triggersResponse } = useAppTriggers(isWorkflowMode ? appId : undefined, {
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes cache
|
||||
@ -261,6 +298,8 @@ const WorkflowAppWithAdditionalContext = () => {
|
||||
const searchParams = useSearchParams()
|
||||
const { getWorkflowRunAndTraceUrl } = useGetRunAndTraceUrl()
|
||||
const replayRunId = searchParams.get('replayRunId')
|
||||
const upgradedFromId = searchParams.get('upgraded_from')
|
||||
const upgradedFromName = searchParams.get('upgraded_from_name')
|
||||
|
||||
useEffect(() => {
|
||||
if (!replayRunId)
|
||||
@ -389,9 +428,19 @@ const WorkflowAppWithAdditionalContext = () => {
|
||||
<>
|
||||
<CollaborationSession />
|
||||
<SandboxMigrationModal
|
||||
show={showMigrationModal && !notSupportMigration}
|
||||
show={showMigrationModal}
|
||||
onClose={handleCloseMigrationModal}
|
||||
onUpgrade={handleUpgradeRuntime}
|
||||
/>
|
||||
{!sandboxEnabled && !upgradedFromId && (
|
||||
<UpgradeRuntimeBanner onUpgrade={handleOpenMigrationModal} />
|
||||
)}
|
||||
{upgradedFromId && (
|
||||
<UpgradedFromBanner
|
||||
fromAppId={upgradedFromId}
|
||||
fromAppName={upgradedFromName || upgradedFromId}
|
||||
/>
|
||||
)}
|
||||
<WorkflowWithDefaultContext
|
||||
edges={edgesData}
|
||||
nodes={nodesData}
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useFeatures } from '@/app/components/base/features/hooks'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Divider from '../base/divider'
|
||||
import {
|
||||
@ -25,9 +26,11 @@ const PanelContextmenu = () => {
|
||||
const panelMenu = useStore(s => s.panelMenu)
|
||||
const clipboardElements = useStore(s => s.clipboardElements)
|
||||
const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal)
|
||||
const setShowUpgradeRuntimeModal = useStore(s => s.setShowUpgradeRuntimeModal)
|
||||
const pendingComment = useStore(s => s.pendingComment)
|
||||
const setCommentPlacing = useStore(s => s.setCommentPlacing)
|
||||
const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd)
|
||||
const sandboxEnabled = !!useFeatures(s => s.features.sandbox?.enabled)
|
||||
const { handleNodesPaste } = useNodesInteractions()
|
||||
const { handlePaneContextmenuCancel, handleNodeContextmenuCancel } = usePanelInteractions()
|
||||
const { handleStartWorkflowRun } = useWorkflowStartRun()
|
||||
@ -147,6 +150,22 @@ const PanelContextmenu = () => {
|
||||
{!pipelineId ? t('importApp', { ns: 'app' }) : t('common.importDSL', { ns: 'workflow' })}
|
||||
</div>
|
||||
</div>
|
||||
{!sandboxEnabled && (
|
||||
<>
|
||||
<Divider className="m-0" />
|
||||
<div className="p-1">
|
||||
<div
|
||||
className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-accent hover:bg-state-base-hover"
|
||||
onClick={() => {
|
||||
setShowUpgradeRuntimeModal(true)
|
||||
handlePaneContextmenuCancel()
|
||||
}}
|
||||
>
|
||||
{t('sandboxMigrationModal.upgrade', { ns: 'workflow' })}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -59,6 +59,8 @@ export type WorkflowSliceShape = {
|
||||
setControlPromptEditorRerenderKey: (controlPromptEditorRerenderKey: number) => void
|
||||
showImportDSLModal: boolean
|
||||
setShowImportDSLModal: (showImportDSLModal: boolean) => void
|
||||
showUpgradeRuntimeModal: boolean
|
||||
setShowUpgradeRuntimeModal: (showUpgradeRuntimeModal: boolean) => void
|
||||
fileUploadConfig?: FileUploadConfigResponse
|
||||
setFileUploadConfig: (fileUploadConfig: FileUploadConfigResponse) => void
|
||||
}
|
||||
@ -109,6 +111,8 @@ export const createWorkflowSlice: StateCreator<WorkflowSliceShape> = set => ({
|
||||
setControlPromptEditorRerenderKey: controlPromptEditorRerenderKey => set(() => ({ controlPromptEditorRerenderKey })),
|
||||
showImportDSLModal: false,
|
||||
setShowImportDSLModal: showImportDSLModal => set(() => ({ showImportDSLModal })),
|
||||
showUpgradeRuntimeModal: false,
|
||||
setShowUpgradeRuntimeModal: showUpgradeRuntimeModal => set(() => ({ showUpgradeRuntimeModal })),
|
||||
fileUploadConfig: undefined,
|
||||
setFileUploadConfig: fileUploadConfig => set(() => ({ fileUploadConfig })),
|
||||
})
|
||||
|
||||
@ -305,5 +305,6 @@
|
||||
"types.basic": "Basic",
|
||||
"types.chatbot": "Chatbot",
|
||||
"types.completion": "Completion",
|
||||
"types.workflow": "Workflow"
|
||||
"types.workflow": "Workflow",
|
||||
"upgradeRuntime": "Clone & Upgrade Runtime"
|
||||
}
|
||||
|
||||
@ -1180,10 +1180,13 @@
|
||||
"publishLimit.startNodeDesc": "You’ve reached the limit of 2 triggers per workflow for this plan. Upgrade to publish this workflow.",
|
||||
"publishLimit.startNodeTitlePrefix": "Upgrade to",
|
||||
"publishLimit.startNodeTitleSuffix": "unlock unlimited triggers per workflow",
|
||||
"sandboxMigrationModal.bannerHint": "This app uses classic runtime. Upgrade to sandboxed runtime for enhanced agent capabilities.",
|
||||
"sandboxMigrationModal.description": "This will create a separate copy of your app, without affecting the original.",
|
||||
"sandboxMigrationModal.dismiss": "Dismiss",
|
||||
"sandboxMigrationModal.title": "Upgrade to filesystem-based agents",
|
||||
"sandboxMigrationModal.upgrade": "Clone & Upgrade",
|
||||
"sandboxMigrationModal.upgradedFrom": "This app was upgraded from \"{{name}}\"",
|
||||
"sandboxMigrationModal.viewOriginal": "View original app",
|
||||
"sidebar.exportWarning": "Export Current Saved Version",
|
||||
"sidebar.exportWarningDesc": "This will export the current saved version of your workflow. If you have unsaved changes in the editor, please save them first by using the export option in the workflow canvas.",
|
||||
"singleRun.back": "Back",
|
||||
|
||||
@ -305,5 +305,6 @@
|
||||
"types.basic": "基础编排",
|
||||
"types.chatbot": "聊天助手",
|
||||
"types.completion": "文本生成",
|
||||
"types.workflow": "工作流"
|
||||
"types.workflow": "工作流",
|
||||
"upgradeRuntime": "复制并升级运行时"
|
||||
}
|
||||
|
||||
@ -1180,10 +1180,13 @@
|
||||
"publishLimit.startNodeDesc": "您已达到此计划上每个工作流最多 2 个触发器的限制。请升级后再发布此工作流。",
|
||||
"publishLimit.startNodeTitlePrefix": "升级以",
|
||||
"publishLimit.startNodeTitleSuffix": "解锁每个工作流无限制的触发器",
|
||||
"sandboxMigrationModal.bannerHint": "当前应用使用经典运行时,升级到沙箱运行时可获得增强的智能体能力。",
|
||||
"sandboxMigrationModal.description": "这将创建你的应用的一个独立副本,不会影响原应用。",
|
||||
"sandboxMigrationModal.dismiss": "暂不升级",
|
||||
"sandboxMigrationModal.title": "升级到基于文件系统的智能体",
|
||||
"sandboxMigrationModal.upgrade": "复制并升级",
|
||||
"sandboxMigrationModal.upgradedFrom": "此应用由「{{name}}」升级而来",
|
||||
"sandboxMigrationModal.viewOriginal": "查看原应用",
|
||||
"sidebar.exportWarning": "导出当前已保存版本",
|
||||
"sidebar.exportWarningDesc": "这将导出您工作流的当前已保存版本。如果您在编辑器中有未保存的更改,请先使用工作流画布中的导出选项保存它们。",
|
||||
"singleRun.back": "返回",
|
||||
|
||||
@ -111,6 +111,10 @@ export const copyApp = ({
|
||||
return post<AppDetailResponse>(`apps/${appID}/copy`, { body: { name, icon_type, icon, icon_background, mode, description } })
|
||||
}
|
||||
|
||||
export const upgradeAppRuntime = (appID: string): Promise<{ result: string, new_app_id?: string, converted_agents?: number, skipped_agents?: number }> => {
|
||||
return post(`apps/${appID}/upgrade-runtime`)
|
||||
}
|
||||
|
||||
export const exportAppConfig = ({ appID, include = false, workflowID }: { appID: string, include?: boolean, workflowID?: string }): Promise<{ data: string }> => {
|
||||
const params = new URLSearchParams({
|
||||
include_secret: include.toString(),
|
||||
|
||||
Reference in New Issue
Block a user